zeaven / laravel-easy-suit
easy setup package
Requires
- php: ^8.0.2
- hidehalo/nanoid-php: ^1.1
Suggests
- meilisearch/meilisearch-php: ^0.23.2
- tymon/jwt-auth: dev-develop
README
这是一个为了方便使用Laravel框架开发api而提供的简单封装套件。集合了参数验证、统一返回格式、错误码定义、Sanctum和JWT、日志、代码生成。
Table of contents generated with markdown-toc
安装
composer require zeaven/laravel-easy-suit
发布
php artisan vendor:publish --provider=Zeaven\\EasySuit\\ServiceProvider
Postman 代码生成器
使用前先在.env文件添加postman的apiToken:
POSTMAN_API_TOKEN=xxx
然后执行 artisan pm:run
postman接口定义如下:
其他文件不一一展示,接口代码生成后,只需要配置参数验证规则,和在Logics目录编写业务逻辑代码即可
路由配置自动添加,但是中间件需要自行配置
同时需要修改RouteServiceProvider.php的路由代码如下:
public function boot() { $this->configureRateLimiting(); $this->routes(function () { Route::configRoute('api', 'api'); Route::configRoute('web'); }); }
你也可以按照configRoute方法的写法定义路由:
Route::macro( 'configRoute', function (string $name, string $prefix = '/', array $middleware = []) { $namespace = "App\\Http\\Controllers\\" . ucfirst($name); Route::prefix($prefix) ->middleware(empty($middleware) ? $name : $middleware) ->namespace($namespace) ->domain(config('app.url')) ->group(base_path("routes/{$name}.php")); } );
Request封装
BaseRequest继承于FormRequest,增加一个rule方法配置参数规则。
定义Request对象
use Zeaven\EasySuit\Http\Requests\BaseRequest; class LoginRequest extends BaseRequest { /** * 返回参数验证规则. * * @return array */ protected function rule(): array { return [ // 用户名 'username' => ['rule' => 'required'], // 密码 'password' => ['rule' => 'required|min:6'] ]; } }
Request对象提供两个方法获取参数:params和values,注意:未配置的参数是无法通过这两个方法获取的
[$username, $password] = $request->values(); // 或 [$username] = $request->values(['username']); // 或 [$password, $username] = $request->values(['password', 'username']); // 获取key/value数组 $params = $request->params(); // $params = ['username' => 'xxx', 'password' => 'xxx'] // 或 $params = $request->params(['username']); // 或 // $params = ['username' => 'xxx'] $params = $request->params(['username', 'password']); // $params = ['username' => 'xxx', 'password' => 'xxx']
Request对象参数配置
rule方法返回参数的配置,完整配置字段如下:
[ 'username' => [ 'rule' => 'required', 'default' => 'admin', 'type' => 'string', 'as' => 'login_name' ], 'remember' => true, // 等同于 'remember' => ['default' => true] 'password' // 等同于‘password’ => ['default' => null] ]
- rule 与Laravel的表单验证规则一致;
- default 默认值;
- type 参数类型,可选值有:int、float、bool、array(json)、date、ip、url、split(将字符串转以逗号分割成数组);
- as 别名,使用values()方法返回的key值;
全局返回统一格式
在使用前需要先添加路由中间件:
protected $middlewareGroups = [ 'web' => [ ], 'api' => [ \Zeaven\EasySuit\Http\Middleware\GlobalResponse::class, // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
在easy_suit.php配置文件中有如下默认配置:
'global_response' => [ 'fields' => [ 'code' => 'code', 'data' => 'data', 'message' => 'msg', 'error' => false, // error只有在debug环境下有效 ], 'exclude' => [ 'horizon/*', 'laravel-websockets/*', 'broadcasting/*', '*/export/*', '*/pusher/auth', '*/pusher/auth', 'web/*', ] ],
fields 指定返回的字段,以及字段名称,如果定义为false则不显示 exclude 可定义排除的路由
在控制器中 调用ok()全局方法,返回即可。
错误码和异常抛出
错误码定义
在error_code.php配置文件中定义错误码信息,下面的规则只是参考,至于你喜欢多少位的错误码,完全由你决定
错误码会发布到lang目录下,因为它是支持多语言的。
/** * 错误码定义样例,请不要在这里定义错误码!应在对应的语言包目录下创建error_code.php文件! * 错误码以十六进制方式定义,如f00000: * -- 第1位:项目 f全局、1~e自行分项目,如1ios、2android、3web、4小程序 * -- 第2-3位:模块 00全局、其他数字自行定义,如01登录、02订单、03钱包 * -- 第4-5位:错误码,如00~ff都可以使用 * -- 第6位:提示码,0忽略错误、1客户端弹窗提示、2客户端toast提示 * 出现error_code=0,则后台未知错误 * * 比如定义ios端登录模块错误码: 101012 => '用户名错误', 101022 => '用户密码错误' */ return [ '401' => '未授权', '500' => '查询出错', 'f00002' => 'token已过期', 'f00012' => '用户不存在', 'f00022' => 'token无效', 'f00032' => '缺少登录信息', ];
异常抛出
使用全局方法在你需要的地方抛出异常
// 直接抛出错误码 throw_e(0x000001); throw_e(401); // 抛出异常信息 throw_e('异常信息'); // 指定错误信息和错误码 throw_e('异常信息', 0x000001); // 空条件抛出 throw_empty($user, 0x000001); // $user变量为空则抛出异常 throw_empty($user, '异常信息'); // 同上 throw_empty($user, '异常信息', 0x000001); // 同上 // 判断条件抛出 throw_on($user->status === -1, 0x000001); // 或 throw_on($user->status === -1, '异常信息'); throw_on($user->status === -1, '异常信息', 0x000001);
所有异常抛出方法的最后一个参数可以传入一个数组,用于本地化参数替换,替换翻译字符串中的参数
注解日志
注解日志采用控制器方法添加注解的方式实现,在使用前需要先添加路由中间件:
protected $middlewareGroups = [ 'web' => [ ], 'api' => [ \Zeaven\EasySuit\Annotations\AnnoLogMiddleware::class, // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
日志默认使用laravel的日志服务,你可以在easy_suit.php配置文件中修改自定义的处理程序,以及是否开启日志
'anno_log' => [ 'enable' => env('EASY_SUIT_ANNO_LOG', true), 'handler' => MyAnnoLogHandler::class ],
MyAnnoLogHandler 对象必须实现接口 \Zeaven\EasySuit\Annotations\AnnoLogHandler;
在控制器使用日志
当此方法有请求时,日志会在返回用户结果后保存
use Zeaven\EasySuit\Annotations\AnnoLog; // 必须引用注解命名空间 #[AnnoLog(tpl:"{mobile},审核提现,订单号{order_no},签名{sign}")] public function index(TestRequest $request) { // 设置日志模板变量 anno_log(['order_no' => 'test', 'sign' => 'sign']); // 或 anno_log('order_no', 'test'); anno_log('sign', 'sign'); }
内置注解模板变量
在用户登录状态下,登录用户模型的缓存字段信息将自动添加到模板变量,可直接使用,如:
- uid
- mobile
- username
- nickname
用户认证
配置
在easy_suit.php文件中
'auth' => [ 'sanctum' => true, 'jwt' => [ 'enable' => true, 'guard' => 'jwt' ], ],
自动刷新Token
内置的Authenticate对Token验证的同时,如果token超过刷新周期,
则会自动刷新对应的Token,通过响应头下发给客户端,所以客户端应该在请求成功回调中,判断响应头是否包含“Authorization”字段,有的话,记得刷新本地Token。
$axios.onResponse((response) => { // 刷新token if ('authorization' in response.headers) { // eslint-disable-next-line no-unused-vars const [_, token] = response.headers.authorization.split(' ') store.commit('SET_TOKEN', token) } return Promise.resolve(response.data) })
注:自动刷新token必须在客户端请求中添加Authorization请求头
Sanctum认证
在配置文件中启用Sanctum认证后,将会使用内置的Authenticate接管认证过程,
同时为Sanctum认证增加自动刷新token功能,具体配置项如下;
'expiration' => 20160, // 两周过期时间 'refresh_ttl' => 60, // 一个小时刷新一次token 'refresh_grace_ttl' => 5, // 刷新token的灰色时间,防止同一token并发多个请求刷新多次 'remove_refresh_expire_token' => true, // 是否移除已刷新的token如上配置,token将会在每小时刷新一次,每次有效期是两周 即两周内有访问,token有效期就可以一直往后延 每次刷新后,原来的token也可以选择是否需要删除
使用Sanctum认证
在路由配置中添加auth中间件
Route::middleware('auth:sanctum')->group(function () { // 你的路由 });
JWT认证
在配置文件中启用JWT认证后,将会使用内置的Authenticate接管认证过程,
同时为JWT认证增加自动刷新token功能。
安装JWT第三方包
Laravel 9.x 不支持 tymon/jwt-auth 包,但可以指定开发版
composer require "tymon/jwt-auth:dev-develop"
配置JWT
按照tymon/jwt-auth配置,在auth.config添加jwt的守卫配置后
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'jwt' => [ 'driver' => 'jwt', 'provider' => 'users', ] ],
在easy_suit.php配置文件中,也把auth.jwt.guard改成你添加的守卫名称,这里都是"jwt"
使用JWT认证
在路由配置中添加auth中间件
Route::middleware('auth:jwt')->group(function () { // 你的路由 });
ResponseMapper 资源映射
对应的是Laravel的API 资源,使用的场景不多,所以采用配置的方式将数据转换成JSON格式。
Mapper生成
可以通过artisan命令生成Maaper文件
php artisan gen:mapper user/info // 将在路径生成文件 App/Http/ResponseMappers/User/InfoMapper.php <?php namespace App\Http\ResponseMappers\User; use Zeaven\EasySuit\Http\ResponseMappers\BaseResponseMapper; class InfoMapper extends BaseResponseMapper { protected $mapper = []; protected $hidden = []; }
Mapper配置
mapper属性配置字段映射 hidden属性配置字段隐藏
假定当前数据模型如下:
$article = [ 'title' => 'xxx', 'content' => 'xxx', 'tags' => '', 'comments' => [ [ 'user' => ['username' => '用户A', 'vip' => 'x', 'mobile' => 'xxx'], 'content' => 'xxx' ], ], 'category' => ['name' => 'xxx'] 'user' => ['username' => '作者', 'vip' => 'x'] ];
希望返回的JSON格式如下:
{ "title": "xxx", "content": "xxx", "comments": [ { "user": {"username": "用户A", "mobile": "xx***xx"}, "content": "xxx" } ], "category_name": "xxx", "poster_username": "作者", "poster_vip": "x" }
对应的mapper配置:
class InfoMapper extends BaseResponseMapper { protected $mapper = [ // 将模型中category对象的name属性,转换为JSON数据的category_name字段 'category_name': 'category.name', // 将模型中user对象的所有性情展开到JSON对象下,并添加"poster_"前缀 // 将"poster"改为"_",则不添加前缀,直接把user对象属性复制到JSON对象下 'poster' => 'user.*', // 对模型中comments数组的每一个对象的mobile属性值,传递给指定handler处理后返回 // Handler可指定多个 'comments.*' => ['user.mobile', MobileHandler::class] ]; protected $hidden = [ "tags", "comments.*.user.vip", "user", "category" ]; } // MobileHandler class MobileHandler { // $value指定的属性值/或上一个handler返回值, $data为当前属性的对象数据,如上面的配置即为comment的user数据 public function handle($value, $data) { return ''; } }
使用
直接在控制器中返回
return new InfoMapper($article);
Model扩展
在easy_suit.php配置扩展开关
'model' => [ // 或 'simple_pagination' => '自定义分页对象' 'simple_pagination' => true, 'extension' => true ]
开启分页简化
Laravel默认分页对象返回的字段过多,开启简化后只返回两个字段"items"、"total"
开启扩展
为Laravel Model增加几个扩展方法
withs
Laravel 9.18 版本以上推荐直接使用官方的with方法。
withs 用于加载嵌套模型,假如有模型关系 A<-B<-C<-D。
A::with('b.c.d') // 等同于 A::withs('b', 'c', 'd') // 等同于 A::withs(['b', 'c', 'd']) // 加载关系模型时选择字段 A::withs('b:col1,col2,col3', 'c', 'd:col1,col2') // 等同于 A::withs(['b:col1,col2,col3', 'c', 'd:col1,col2']) // 载关系模型时增加筛选条件 A::withs([ 'b:col1,col2,col3' => fn($q) => $q->where('col1', 'xxx'), 'c' => fn($q) => $q->select('col1','col2','col3'), 'd:col1,col2' => fn($q) => $q->orderBy('col3') ])
selectWhen
如果指定字段数组不为空则使用,否则返回所有字段
$fields = []; A::selectWhen($fields)->get();
whereWhen
如果指定过滤条件数组不为空则使用,常用在表格筛选提交的条件中
$options = ['col1' => 1, 'col2' => 'xxx']; A::whereWhen($options)->get();
whenFilled
当给定参数有值时,执行回调
$a = 1; $col1 = 'xxx'; A::whenFilled($a, $col1, function($q, $a, $col1) => { $q->where('col1', $col1)->where('col2', $a); })->get();
betweenWhen
A::betweenWhen('col1', 1, 10)->get(); // select * from a where col1 between 1, 10 A::betweenWhen('col1', 1)->get(); // select * from a where col1 >= 1 A::betweenWhen('col1', null, 10)->get(); // select * from a where col1 <= 10
likeWhen
当给定参数有值,则对指定字段模糊查询
A::likeWhen('col1', 'xyz')->get(); // select * from a where col1 like '%xyz%'