当前位置: 首页 > news >正文

让php开发更优雅-Laravel篇

前言


随着开发经验的增加,也伴随团队开发的积累,规范开发显得越来越重要,本文给大家提供一些laravel开发的进阶思路和经验,让大家开发更加统一规范,代码看起来更加优雅。

1.更多使用第三方库。团队开发的时候,各个人的开发经验和思考方式不同,也有一些思维局限性,可以多多使用第三方库,统一代码开发规范。

2.抛弃传统MVC模式,在结构上分出更新的结构分层,进行代码解耦。一般分为下面几层:

  • 数据验证层(Request):负责验证请求参数,单独创建一个验证请求器,避免验证逻辑放在控制器中验证,例如:

    Laravel框架使用FormRequest Laravel中使用FormRequest进行表单验证及对验证异常进行自定义处理_51CTO博客_laravel 表单验证

    TP框架使用FormRequest thinkphp6 FormRequest,laravel 表单验证_think\request和laravel-CSDN博客

  • 控制器层(Controller):负责接收参数、验证参数、调用各个模块的服务层(可以用事务包裹,用到其他服务的可以注入多个服务)、返回响应、返回视图等

  • 服务层(Service):负责具体的业务逻辑实现,将原本控制器的负责流程按模块拆分为一个个小的服务,方便给控制器层组合调用,一般不要跨模块调用服务,服务中可以调用本模块的仓库层方法

  • 仓库层(Repository):调用模型层封装一些负责的查询,方便服务层调用,一般负责只查询本模块的内容

  • 模型层(Model):只包含默认的表属性(表名、字段等)和联表关系

  • (更多层级)

环境搭建


为了不重复造轮子和方便命令行创建文件,本文直接用laravel自带的FormRequest和第三方phpno1-architecture这两个包进行开发示例,这两个库的具体使用可以参考下面几篇文章:

https://learnku.com/articles/9763/open-source-wheel-laravel-project-architecture-extension-package

Laravel中使用FormRequest进行表单验证及对验证异常进行自定义处理_51CTO博客_laravel 表单验证

Laravel实现表单验证(多场景,适用于API)_laravel formrequest scenes-CSDN博客

  • 创建laravel项目

    composer create-project laravel/laravel your_project_name
    
  • 安装phpno1/architecture并发布配置

    composer require phpno1/architecture
    php artisan vendor:publish --provider "Phpno1\Architecture\Providers\ArchitectureServiceProvider"
    

    如有需要,可以修改config/architecture.php中的配置

  • 注册到服务容器

    # 在config/app.php中'providers' => [// ......App\Providers\ArchitectureServiceProvider::class,];
    
  • 如果你的laravel或php版本比较高,可能有部分辅助函数已经没有了,那么你需要添加自定义函数去代替它们,例如在app/Helpers/functions.php添加下面的辅助函数

    <?php
    /*** 自定义 ends_with 函数* @param $haystack* @param $needle* @return bool*/
    function ends_with($haystack, $needle) {return str_ends_with($haystack, $needle);
    }/*** 下划线转驼峰命名法* @param $string* @return string*/
    function camel_case($string){$parts = explode('_', $string);foreach ($parts as $index => $part) {$parts[$index] = ucfirst($part);}$camelCaseString = lcfirst(implode('', $parts));return $camelCaseString;
    }/*** 驼峰命名法转下划线命名法* @param $string* @return string*/
    function snake_case($string) {// 使用正则表达式将字符串转换为小写,并将非单词字符替换为下划线$snake = strtolower(preg_replace('/[^A-Za-z0-9-]+/', '_', $string));// 去除开头和结尾的下划线$snake = trim($snake, '_');return $snake;
    }

    然后修改文件composer.json,在autoload节点添加自定义函数文件

    "autoload": {"psr-4": {"App\\": "app/","Database\\Factories\\": "database/factories/","Database\\Seeders\\": "database/seeders/"},"files": ["app/Helpers/functions.php"]},
    

    最后执行composer dump-autoload使其生效即可

开发示例


以用户注册流程为例

统一响应

这里为了统一响应格式,直接用第三方封装的响应数据包,具体用法参考官方文档:

laravel-response: 🤖 Provide a standardized and unified response data format for Laravel and Lumen API projects. - 为 Laravel 和 Lumen API 项目提供一个规范统一的响应数据格式。 (gitee.com)

示例

Response::fail();
Response::ok();
Response::success([]);

控制器

创建控制器

php artisan make:controller UserController

控制器校验请求后调用不同 service 进行业务处理,调用 API Resource 转换资源数据返回,示例:

<?phpnamespace App\Http\Controllers;use App\Http\Requests\UserRequest;
use App\Services\SmsService;
use App\Services\UserService;
use Jiannei\Response\Laravel\Support\Facades\Response;class UserController extends Controller
{private $userId; //用户idprivate $userService; //用户业务层public function __construct(UserService $userService){$this->userService = $userService;$this->userId = 1;}/*** 获取用户信息*/public function info(){return Response::success($this->userService->getUserInfo($this->userId));}/*** 注册* * @param UserRequest $request* @param SmsService $smsService* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Resources\Json\JsonResource* @throws \Illuminate\Auth\Access\AuthorizationException* @throws \Illuminate\Validation\ValidationException*/public function register(UserRequest $request, SmsService $smsService){//参数验证$request->validate('register');//短信验证码验证if (!$smsService->check($request->mobile, 'register', $request->code)){return Response::fail('验证码错误');}try{//新增用户$this->userService->addUser($request->all());//绑定推荐人...//其他服务操作...return Response::ok();}catch (\Exception $e){return Response::fail($e->getMessage());}}
}

重点事项:

  • 将注入的Illuminate\Http\Request换成自定义的FormRequest

验证器

创建基础验证器

php artisan make:request BaseRequest

创建的验证器在app/Http/Requests/BaseRequest.php

然后修改其内容为:

<?phpnamespace App\Http\Requests;use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
use Jiannei\Response\Laravel\Support\Facades\Response;/*** 基础验证器* Class AbstractRequest* @package App\Http\Requests*/
class BaseRequest extends FormRequest
{public $scenes = [];                //场景集合public $currentScene;               //当前场景public $autoValidate = false;       //是否注入之后自动验证public function authorize(){return true;}/*** 设置场景* @param $scene* @return $this*/public function scene($scene){$this->currentScene = $scene;return $this;}/*** 覆盖自动验证方法*/public function validateResolved(){if ($this->autoValidate) {$this->handleValidate();}}/*** 验证方法* @param string $scene* @throws \Illuminate\Auth\Access\AuthorizationException* @throws \Illuminate\Validation\ValidationException*/public function validate($scene = ''){if ($scene) {$this->currentScene = $scene;}$this->handleValidate();}/*** 根据场景获取规则* @return array|mixed*/public function getRules(){$rules = $this->container->call([$this, 'rules']);$newRules = [];if ($this->currentScene && isset($this->scenes[$this->currentScene])) {$sceneFields = is_array($this->scenes[$this->currentScene])? $this->scenes[$this->currentScene] : explode(',', $this->scenes[$this->currentScene]);foreach ($sceneFields as $field) {if (array_key_exists($field, $rules)) {$newRules[$field] = $rules[$field];}}return $newRules;}return $rules;}/*** 覆盖设置 自定义验证器* @param $factory* @return mixed*/public function validator($factory){return $factory->make($this->validationData(), $this->getRules(),$this->messages(), $this->attributes());}/*** 最终验证方法* @throws \Illuminate\Auth\Access\AuthorizationException* @throws \Illuminate\Validation\ValidationException*/protected function handleValidate(){$instance = $this->getValidatorInstance();if ($instance->fails()) {$this->failedValidation($instance);}$this->passedValidation();}/*** 重写验证失败返回*/protected function failedValidation(Validator $validator){$error = $validator->errors()->all();throw new ValidationException(Response::errorBadRequest($error[0]));}
}

接下来创建其他验证器的时候继承BaseRequest就可以了,比如创建一个UserRequest

php artisan make:request UserRequest

然后修改其内容为:

<?phpnamespace App\Http\Requests;/*** 用户模块验证*/
class UserRequest extends BaseRequest
{/*** Determine if the user is authorized to make this request.** @return bool*/public function authorize(){return true;}/*** 验证规则* Get the validation rules that apply to the request.** @return array*/public function rules(){return ['mobile' => 'required|regex:/^1[3-9]\d{9}$/','password' => 'required|min:6',];}/*** 验证字段* Get custom attributes for validator errors.** @return array*/public function attributes(){return ['mobile' => '手机号码','password' => '登录密码'];}/*** 自定义提示信息* Get custom messages for validator errors.** @return array*/public function messages(){return ['*.required' => ':attribute不能为空','*.regex' => ':attribute格式不正确','*.min' => ':attribute不能少于6位',];}/*** 定义验证场景和对应的验证字段*/public $scenes = [//注册'register' => ['mobile','password'],//登录'login' => ['mobile','password'],];
}

服务

创建服务

php artisan phpno1:service User

上面的命令会生成app/Services/UserService.php,他会自动绑定对应的仓库App\Repository\Contracts\UserRepository

<?phpnamespace App\Services;use App\Repository\Contracts\UserRepository;class UserService
{/*** @var UserRepositoryEloquent*/protected $userRepository;public function __construct(UserRepository $userRepository){$this->userRepository = $userRepository;}/*** 添加用户*/public function addUser($data){$this->userRepository->create($data);}/*** 查找用户* @param $id* @return \Illuminate\Database\Eloquent\Builder*/public function getUserInfo($id){return $this->userRepository->find($id);}
}

仓库

创建仓库

php artisan phpno1:repository User

执行命令之后会生成app/Repository/Contracts/UserRepository.phpapp/Repository/Eloquent/UserRepositoryEloquent.php;我们平时写数据库查询逻辑在UserRepositoryEloquent中写就可以了

模型

创建模型

php artisan make:model User

模型里面只保留默认的表属性和关联绑定方法即可,不处理业务

<?phpnamespace App\Models;use App\Traits\SerializeDate;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;class User extends Authenticatable
{use HasApiTokens, HasFactory, Notifiable, serializeDate;/*** The attributes that are mass assignable.** @var array<int, string>*/protected $fillable = ['name','mobile','password',];/*** The attributes that should be hidden for serialization.** @var array<int, string>*/protected $hidden = ['password','updated_at','remember_token','email_verified_at',];/*** The attributes that should be cast.** @var array<string, string>*/protected $casts = [];
}

相关文章:

让php开发更优雅-Laravel篇

前言 随着开发经验的增加&#xff0c;也伴随团队开发的积累&#xff0c;规范开发显得越来越重要&#xff0c;本文给大家提供一些laravel开发的进阶思路和经验&#xff0c;让大家开发更加统一规范&#xff0c;代码看起来更加优雅。 1.更多使用第三方库。团队开发的时候&#xf…...

自动化测试之httprunner框架hook函数实操

本篇介绍httprunner中hook函数的使用&#xff0c;以及通过编程能力实现建设自动化测试更全面的场景覆盖 前置&#xff1a; 互联网时代让我们更快的学习到什么是Httprunner 正文&#xff1a; 经过上文了解到这个框架怎么使用之后&#xff0c;我们开始来探讨一下我们为什么要用…...

物联网实战--入门篇之(七)嵌入式-MQTT

目录 一、MQTT简介 二、MQTT使用方法 三、MQTT驱动设计 四、代码解析 五、使用过程 六、总结 一、MQTT简介 MQTT因为其轻量、高效和稳定的特点&#xff0c;特别适合作为物联网系统的数据传输协议&#xff0c;已经成为物联网事实上的通信标准了。关于协议的具体内容看看这…...

跑模型——labelme的json文件转成yolo使用的txt文件(语义分割)

前言 将labelme多边形标注的json文件转换成yolo使用的txt文件 import os import json import numpy as np from tqdm import tqdm#实现函数 def json2txt(path_json, path_txt): # 可修改生成格式with open(path_json, r) as path_json:jsonx json.load(path_json)with open…...

一个项目仿京东商场代码

git clone http://git.itcast.cn/heimaqianduan/erabbit-uni-app-vue3-ts.git...

计算机网络——WEB服务器编程实验

实验目的 1. 处理一个 http 请求 2. 接收并解析 http 请求 3. 从服务器文件系统中获得被请求的文件 4. 创建一个包括被请求的文件的 http 响应信息 5. 直接发送该信息到客户端 具体内容 一、C 程序来实现 web 服务器功能。 二、用 HTML 语言编写两个 HTML文件&#xff0c;并…...

蓝桥杯算法题:最大比例

题目描述&#xff1a; X星球的某个大奖赛设了 M 级奖励。 每个级别的奖金是一个正整数。 并且&#xff0c;相邻的两个级别间的比例是个固定值。 也就是说&#xff1a;所有级别的奖金数构成了一个等比数列。 比如&#xff1a;16,24,36,54&#xff0c;其等比值为&#xff1a;3/2。…...

【堡垒机】堡垒机的介绍

目前&#xff0c;常用的堡垒机有收费和开源两类。 收费的有行云管家、纽盾堡垒机&#xff1b; 开源的有jumpserver&#xff1b; 这几种各有各的优缺点&#xff0c;如何选择&#xff0c;大家可以根据实际场景来判断 什么是堡垒机 堡垒机&#xff0c;即在一个特定的网络环境下&…...

通过 ffmpeg命令行 调节视频播放速度

1. 仅调整视频速率 视频调速原理&#xff1a;修改视频的pts&#xff0c;dts # 可能会丢帧 ffmpeg -i input.mkv -an -filter:v "setpts0.5*PTS" output.mkv # 可用-r参数指定输出视频FPS以防止丢帧 ffmpeg -i input.mkv -an -r 60 -filter:v "setpts2.0*PTS&q…...

SQLite数据库在Linux系统上的使用

SQLite是一个轻量级的数据库解决方案&#xff0c;它是一个嵌入式的数据库管理系统。SQLite的特点是无需独立的服务器进程&#xff0c;可以直接嵌入到使用它的应用程序中。由于其配置简单、支持跨平台、服务器零管理&#xff0c;以及不需要复杂的设置和操作&#xff0c;SQLite非…...

Spring中依赖注入的方法有几种,分别是什么?

依赖注入的目的&#xff1a; 都是为了减少对象之间的紧密耦合 1. 构造函数注入&#xff1a;通过在类的构造函数中接受依赖对象作为参数&#xff0c;Spring在创建对象时将依赖注入。 2. Setter方法注入&#xff1a;在类中提供setter方法&#xff0c;Spring通过调用这些setter方法…...

【面试精讲】MyBatis设计模式及源码分析,MyBatis设计模式实现原理

【面试精讲】MyBatis设计模式及源码分析&#xff0c;MyBatis设计模式实现原理 目录 本文导读 一、MyBatis中运用的设计模式详解 1. 工厂模式&#xff08;Factory Pattern&#xff09; 2. 单例模式&#xff08;Singleton Pattern&#xff09; 3. 建造者模式&#xff08;Bu…...

Acrel-1000DP光伏监控系统在尚雷仕(湖北)健康科技有限公司5.98MW分布式光伏10KV并网系统的应用

摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式多为自发自用&#xff0c;余电上网&#xff0c;部分项目采用全额上网模式。分布式光伏全额上网的优点是可以充分利用分布式光伏发电系统的发电量&#xff0c;提高分布式光伏发电系统的利用率。发展分…...

电脑远程控制esp32上的LED

1、思路整理 首先esp32需要连接上wifi 然后创建udp socket 接受udp数据 最后解析数据&#xff0c;控制LED 2、micropython代码实现 import network from socket import * from machine import Pin p2Pin(2,Pin.OUT)def do_connect(): #连接wifi wlan network.WLAN(network.…...

ARXML处理 - C#的解析代码(一)

目的 本文介绍通过AUTOSAR组织提供的xsd文件&#xff0c;自动生成对应的C#解析代码的框架。 自动生成方法&#xff1a;Microsoft SDKs\Windows\v7.0A\bin\xsd.exe 命令&#xff1a;xsd.exe AUTOSAR_4-0-3.xsd /c /l:CS /n:AUTOSAR4 AUTOSAR_4-0-3.xsd 是需要生成代码的xsd文…...

OJ 栓奶牛【C】【Python】【二分算法】

题目 算法思路 要求的距离在最近木桩与最远木桩相隔距离到零之间&#xff0c;所以是二分法 先取一个中间值&#xff0c;看按照这个中间值可以栓多少奶牛&#xff0c;再与输入奶牛数比较&#xff0c;如果大于等于&#xff0c;则增大距离&#xff0c;注意这里等于也是增大距离…...

Spring6-单元测试:JUnit

1. 概念 在进行单元测试时&#xff0c;特别是针对使用了Spring框架的应用程序&#xff0c;我们通常需要与Spring容器交互以获取被测试对象及其依赖。传统做法是在每个测试方法中手动创建Spring容器并从中获取所需的Bean。以下面的两行常见代码为例&#xff1a; ApplicationCo…...

ubuntu系统安装k8s1.28精简步骤

目录 一、规划二、环境准备2.1 配置apt仓库配置系统基本软件仓库配置k8s软件仓库安装常用软件包 2.2 修改静态ip、ntp时间同步、主机名、hosts文件、主机免密2.3 内核配置2.4 关闭防火墙、selinux、swap2.5 安装软件安装docker安装containerd安装k8s软件包 三、安装配置k8s3.1 …...

探讨Java和Go语言的缺点

文章目录 Java的缺点Go语言的缺点 通常我们都会讨论Java和GO的优点&#xff0c;如果讨论缺点往往能让人们更清楚优点的重要性&#xff0c;Java和Go的缺点或许往往就是对方优点所在 Java的缺点 冗长的代码&#xff1a;相较于一些现代编程语言&#xff0c;Java 的语法相对冗长&am…...

短剧在线搜索PHP网站源码

源码简介 短剧在线搜索PHP网站源码&#xff0c;自带本地数据库500数据&#xff0c;共有6000短剧视频&#xff0c;与短剧猫一样。 搭建环境 PHP 7.3 Mysql 5.6 安装教程 1.上传源码到网站目录中 2.修改【admin.php】中&#xff0c; $username ‘后台登录账号’; $passwor…...

Python map遍历

在Python中&#xff0c;map 函数是一个内置函数&#xff0c;它将指定的函数应用于给定序列&#xff08;如列表、元组等&#xff09;的每个项&#xff0c;并返回一个迭代器&#xff0c;该迭代器包含所有项经过指定函数处理后的结果。 ### map 函数的基本用法 map 函数的语法如…...

数据结构—红黑树

红黑树介绍 红黑树&#xff08;Red Black Tree&#xff09;是一种自平衡二叉查找树。由于其自平衡的特性&#xff0c;保证了最坏情形下在 O(logn) 时间复杂度内完成查找、增加、删除等操作&#xff0c;性能表现稳定。 在 JDK 中&#xff0c;TreeMap、TreeSet 以及 JDK1.8 的 …...

MES实施之工控机和电脑的选择

在MES项目实施过程中,经常会碰到工控机和电脑的选型问题,那么他们的区别是什么? 1、控机和普通个人电脑(PC)相比,具有以下几个区别: 1.运行环境不同:工控机通常需要在各种恶劣的工业环境中运行,如高温、高湿、强电磁干扰等,因此需要具有防尘、防水、抗干扰等特点。而…...

京东云服务器4核8G主机租用价格418元一年,1899元3年

京东云轻量云主机4核8G服务器租用价格418元一年&#xff0c;1899元3年&#xff0c;配置为&#xff1a;轻量云主机4C8G-180G SSD系统盘-5M带宽-500G月流量&#xff0c;京东云主机优惠活动 yunfuwuqiba.com/go/jd 可以查看京东云服务器详细配置和精准报价单&#xff0c;活动打开如…...

【多模态融合】MetaBEV 解决传感器故障 3D检测、BEV分割任务

前言 本文介绍多模态融合中&#xff0c;如何解决传感器故障问题&#xff1b;基于激光雷达和相机&#xff0c;融合为BEV特征&#xff0c;实现3D检测和BEV分割&#xff0c;提高系统容错性和稳定性。 会讲解论文整体思路、模型框架、论文核心点、损失函数、实验与测试效果等。 …...

[通俗易懂]《动手学强化学习》学习笔记1-第1章 初探强化学习

文章目录 前言第1章 初探强化学习1.1 简介序贯决策&#xff08;sequential decision making&#xff09;任务&#xff1a;强化学习与有监督学习或无监督学习的**区别**&#xff1a;改变未来 1.2 什么是强化学习环境交互与有监督学习的区别1&#xff1a;改变环境 &#xff08;说…...

centOS如何升级python

centOS下升级python版本的详细步骤 1、可利用linux自带下载工具wget下载&#xff0c;如下所示&#xff1a; 笔者安装的是最小centos系统&#xff0c;所以使用编译命令前&#xff0c;必须安装wget服务&#xff0c;读者如果安装的是界面centos系统&#xff0c;或者使用过编译工具…...

【MYSQL锁】透彻地理解MYSQL锁

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1.锁 1.1 概述 1.2 全局锁 1.2.1 语法 1.2.1.1 加全局锁 1.2.1.2 数据备份 1.2.1.3 释放锁 1.2.1.4 特点 1.2.1.5 演示 1.3 表级锁 1.3.1 介绍 …...

【静态分析】静态分析笔记01 - Introduction

参考&#xff1a; BV1zE411s77Z [南京大学]-[软件分析]课程学习笔记(一)-introduction_南京大学软件分析笔记-CSDN博客 ------------------------------------------------------------------------------------------------------ 1. program language and static analysis…...

使用的sql

根据CODE去重 SELECT * FROM ( SELECT count( camera_code ) AS count, camera_code FROM n_camera_basic GROUP BY camera_code ) t WHERE t.count >1 DELETE FROM n_camera_basic WHERE camera_id NOT IN (SELECT dt.minno…...