# laravel-restful **Repository Path**: steve_ma/laravel-restful ## Basic Information - **Project Name**: laravel-restful - **Description**: laravel 中 每个restful接口都需要写一堆方法。我想写一个公共的类 然后继承它 只需要少少的配置就可以输出相关的API接口 - **Primary Language**: PHP - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-07-25 - **Last Updated**: 2023-11-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # laravel-restful ### 介绍 laravel 中 每个restful接口都需要写一堆方法。我想写一个公共的类 然后继承它 只需要少少的配置就可以输出相关的API接口 ### 安装教程 ```shell # 安装 $ composer require stevema/laravel-restful ``` ### 使用说明 ```php # 比如我现在想加个配置相关的表并输出对应的restful接口 # 1.命令行执行代码 $ php artisan make:restful api/common/make_test -m # 其中 api/common 是命名空间 make_test 是想要的模型名称 # 可带参数 -i 生成带说明的文件 -m 生成数据迁移文件 -s 生成数据填充文件 # 执行后输出 INFO Migration [database/migrations/2023_08_17_041228_create_make_tests_table.php] created successfully. INFO model [app/Models/api/common/MakeTest.php] created successfully. INFO resource [app/Http/Resources/api/common/MakeTestResource.php] created successfully. INFO request [app/Http/Requests/api/common/MakeTestRequest.php] created successfully. INFO filter [app/Http/Filters/api/common/MakeTestFilter.php] created successfully. INFO permission [app/Http/Permissions/api/common/MakeTestPermission.php] created successfully. INFO controler [app/Http/Controllers/api/common/MakeTestController.php] created successfully. # 这个时候已经生成了 数据库迁移文件(Migration) 模型(Model) 解释器(Resource) 验证器(Request) 过滤器(Filter) 权限验证(Permission) 控制器(Controller) # 2. 修改migration迁移文件 修改up方法 具体文件看生成的路径 public function up(): void { Schema::create('make_tests', function (Blueprint $table) { $table->comment("测试restful表"); $table->id(); $table->string('field1', 200)->nullable()->comment('field1字段'); $table->string('field2', 200)->default("default")->comment('field2字段'); $table->integer('field3')->default(0)->comment('field3字段'); $table->boolean('is_del')->default(0)->comment('是否删除[0=否,1=是]'); $table->timestamps(); }); } # 3. 执行数据库迁移文件 $ php artisan migrate # 这个时候生成表成功了 当然也可以跳过这一步 手动去创建表 CREATE TABLE `make_tests` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `field1` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'field1字段', `field2` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'default' COMMENT 'field2字段', `field3` int(11) NOT NULL DEFAULT '0' COMMENT 'field3字段', `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除[0=否,1=是]', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='测试restful表'; # 4. 配置模型(Model)文件 把$fillable=[]配置上 具体文件看生成的路径 namespace App\Models\api\common; use Stevema\Restful\RestfulModel; class MakeTest extends RestfulModel { protected $table="make_tests"; /** * 批量赋值的字段 * @var string[] */ protected $fillable = ['field1','field2','field3','is_del']; } # 5.添加路由 路由文件就不放路径了 Route::apiResource('maketest', \App\Http\Controllers\api\common\MakeTestController::class); # 这个时候 php artisan route:list 就可以查看路由了 GET|HEAD api/maketest ...................... maketest.index › api\common\MakeTestController@index POST api/maketest ...................... maketest.store › api\common\MakeTestController@store GET|HEAD api/maketest/{maketest} ........... maketest.show › api\common\MakeTestController@show PUT|PATCH api/maketest/{maketest} ........... maketest.update › api\common\MakeTestController@update DELETE api/maketest/{maketest} ........... maketest.destroy › api\common\MakeTestController@destroy # 简单的restful接口就已经完成了 更多的配置信息请向下看 ``` ### 目录说明 ``` ├─ src │ ├─ Consoles # 命令目录 │ │ └─ Commands # 命令目录 │ │ └─ Stubs # 命令生成文件模版目录 │ ├─ Traits # use 目录 │ │ └─ Destroy.php # 删除方法 │ │ └─ ForceDelete.php # 真实删除方法 │ │ └─ Index.php # 列表方法 │ │ └─ Restore.php # 软删除恢复方法 │ │ └─ Show.php # 详情方法 │ │ └─ SoftDeletes.php # 软删除-模型使用 │ │ └─ SoftDeletingScope.php # 软删除-模型使用 │ │ └─ Store.php # 创建方法 │ │ └─ Update.php # 修改方法 │ └─ PermissionException.php # permission异常 │ └─ RestfulCache.php # restful缓存 │ └─ RestfulController.php # restful控制器 │ └─ RestfulException.php # restful异常 │ └─ RestfulFilter.php # restful过滤器 │ └─ RestfulFilterResource.php # restful过滤解释器 │ └─ RestfulModel.php # restful模型 │ └─ RestfulPermission.php # restful权限验证 │ └─ RestfulProvider.php # restful服务提供者 │ └─ RestfulRequest.php # restful验证器 │ └─ RestfulResource.php # restful解释器 └─ composer.json # 配置 ``` ### 详细配置 #### 控制器-Controller ```php #对于Controller来说 没什么需要改的 有新的接口直接加上然后写路由就行了 # 引入软删除之后 controller中就可以use Restore 和 ForceDelete了 namespace App\Http\Controllers\api\common; Use Stevema\Restful\RestfulController; Use Stevema\Restful\Traits\Index; Use Stevema\Restful\Traits\Update; Use Stevema\Restful\Traits\Store; Use Stevema\Restful\Traits\Show; Use Stevema\Restful\Traits\Destroy; use Illuminate\Http\Request; class MakeTestController extends RestfulController { use Index,Store,Update,Show,Destroy; /** * 路由绑定的参数名称 * ROUTE_KEY 模型在路由中应当保持一致 然后就是应当是Model的小写 * 比如 Route::get('/smorders/{smorder}', function(){}) -> 对应的模型应该是 SmOrder * ROUTE_KEYMAP 关系模型时用到的 * 比如 Route::get('/smorders/{smorder}/smskus/{smsku}', function(){}) * SmOrder 和 SmSku 可以是一对一 也可以是一对多 * 这样就需要 ROUTE_KEY来确认接收哪个参数了 * ROUTE_KEYMAP 来对应相关的值 -> 在smsku 中 如果smorder关联的是 order_id * ROUTE_KEYMAP = ['smorder' => 'order_id] 这样来与smorder对应 * * 比如 Route::get('/smorders/{smorder}', function(){}) -> 对应的模型应该是 SmOrder * 只有一个模型的时候不需要 ROUTE_KEY 和 ROUTE_KEYMAP * * @var string */ protected const ROUTE_KEY = null; protected const ROUTE_KEYMAP = []; /** * 过滤插件 列表传过来的参数需要一些方法来走到模型里面 * 请继承 Stevema\Restful\Filters\RestfulFilter * 可以没有 但是不能乱搞 */ protected const FILTERS = \App\Http\Filters\api\common\MakeTestFilter::class; /** * 模型 对应的表 * 应当继承 Illuminate\Database\Eloquent\Model */ protected const MODEL = \App\Models\api\common\MakeTest::class; /** * 模型解释器 -> 模型查出来的数据 有些不想放出去的 就可以使用这个来处理 * 应当继承 Illuminate\Http\Resources\Json\JsonResource */ protected const RESOURCE = \App\Http\Resources\api\common\MakeTestResource::class; /** * request -> post put patch 使用到的表单验证 - 表单验证还包含权限验证 * 应当继承 Stevema\Restful\RestfulRequest 并且定义了 scene=>[store, update] * 继承其他的验证也可以 */ protected const REQUEST = \App\Http\Requests\api\common\MakeTestRequest::class; /** * 权限验证 */ protected const PERMISSION = \App\Http\Permissions\api\common\MakeTestPermission::class; } # 若是我提供的方法不满足您的使用 可以定义方法直接覆盖 # 或者 把 use 去掉就行了 ``` #### 模型-Model-主要是软删除 ```php # 配置模型(Model)文件 把$fillable=[]配置上 具体文件看生成的路径 # 因为我有设置 is_del 字段 所以把软删除加上 namespace App\Models\api\common; use Stevema\Restful\RestfulModel; use Stevema\Restful\Traits\SoftDeletes; class MakeTest extends RestfulModel { use SoftDeletes; protected $table="make_tests"; /** * 批量赋值的字段 * @var string[] */ protected $fillable = ['field1','field2','field3','is_del']; /** * 设置软删除 */ protected function getSoftDeleteSetting() { return [ "columnName" => 'is_del', "columnType" => 'int', "defaultValue" => 0, "deletedValue" => 1, ]; } } # 引入 Stevema\Restful\Traits\SoftDeletes # 并且设置 protected function getSoftDeleteSetting(){} # 如果想使用laravel给出的软删除 不设置这个也可以 # 如果有设置 请把软删除的字段 放入 $fillable 中 ``` #### 过滤器-Filter ```php # 过滤器目前只给Index方法使用 - 列表才需要过滤器 详情都有主键来处理了 namespace App\Http\Filters\api\common; use Stevema\Restful\RestfulFilter; class MakeTestFilter extends RestfulFilter{ /** * 默认排序规则 多个中间加逗号 比如 "-type,-id" */ protected const DEFAULT_ORDERING = '-id'; /** * 允许的检索字段 keys 空则不限制 所有的参数都可以参与过滤 * 注意这里只有id 没有前面的-号 */ protected const ACCEPT_ORDERINGS = ["id",]; /** * 允许的检索字段 keys 空则不限制 所有的参数都可以参与过滤 */ protected const ACCEPT_FILTER_KEYS = []; /** * 分页用到的页码参数key */ protected const KEY_PAGE = 'page'; /** * 分页用到的每页显示条数参数key */ protected const KEY_SIZE = 'size'; /** * 分页用到的 cursor 参数key */ protected const KEY_CURSOR = 'cursor'; /** * 默认页数 */ protected const DEFAULT_PAGE = 1; /** * 默认条数 */ protected const DEFAULT_SIZE = 15; /** * 排序用到的参数key */ protected const KEY_ORDERING = 'ordering'; /** * 分页方法 noPaginate 无分页 、 paginate 默认分页 、 simplePaginate 简单分页 、 cursorPaginate cursor分页 */ protected const PAGINATOR = 'paginate'; /** * 资源解释器 - 返回之前重新编辑一下输出的数组 * 默认提供一个 可以用 也可以不用 * \Stevema\Restful\RestfulFilterResource::class */ protected const FILTER_RESOURCE = null; /** * 检索 自定义字段 比如 name字段 方法名是 name_filter * 可以获取 Builder 后执行操作 * $queryset = $this->getQuerySet(); * @param $key 字段名 * @param $value 值 * @return void */ // public function name_filter($key, $value){ // $query = $this->getQuery(); // $query->where($key, 'like', "%{$value}%"); // } } # 定义 允许排序的字段 ACCEPT_ORDERINGS=['id','field2']; # 然后 ording=-id 就可以定义排序 => KEY_ORDERING="ording" # 定义 允许过滤的字段 ACCEPT_FILTER_KEYS=['field2','field3']; # 然后 当传入参数 field2=111 的时候就会执行 $query->where('field2', '=', 111); # 当然你也可以自定义执行语句 public function field2_filter($key, $value){ $query = $this->getQuery(); $query->where($key, 'like', "%{$value}%"); } # noPaginate 无分页 、 paginate 默认分页 、 simplePaginate 简单分页 、 cursorPaginate cursor分页 # 3种分页方法和无分页方法 PAGINATOR = 'paginate' 默认 paginate 默认分页 # 不同的方法会输出不同的格式 可以通过定义资源解释器来重新解释输出的格式 # 默认是null 不想自己写的可以使用 Stevema\Restful\Filters\RestfulFilterResource::class 来处理 protected const FILTER_RESOURCE = \Stevema\Restful\Filters\RestfulFilterResource::class; ``` #### 验证器-Request ```php # 验证器和laravel提供的表单验证没什么区别- 我只是添加了scene # 对不同的scene 可以过滤不同的参数 实现一个验证器对应一个Controller # 注意 patch 方式修改的时候 required 会改成 sometimes 来实现部分修改 # 当然你可以自定义表单验证 namespace App\Http\Requests\api\common; use Stevema\Restful\RestfulRequest; use Illuminate\Contracts\Validation\Validator; class MakeTestRequest extends RestfulRequest { // 第一个失败后停止 protected $stopOnFirstFailure = false; /** * 权限验证 false 会抛出权限异常中止请求 * @return bool */ public function authorize(): bool { return True; // $comment = Comment::find($this->route('comment')); // return $comment && $this->user()->can('update', $comment); //路由模型绑定 - - // return $this->user()->can('update', $this->comment); } /** * 规则列表 * [ * 'field1' => ['required','string','between:2,50'], * 'field2' => ['required','string','between:2,20'], * 'field3' => ['required','string','between:2,20'], * 'field4' => [], * ] */ public function rules(): array { return [ 'field1' => ['required','string','between:2,50'], 'field2' => ['required','string','between:2,20'], 'field3' => ['required','int'], ]; } /** * 场景 * [ * 'store' => ['field1', 'field2','field3','field4'], * 'update' =>['field1', 'field2','field3','field4'], * ] * @var array[] */ public $scenes = [ 'store' => ['field1', 'field2','field3'], 'update' => ['field1', 'field2','field3'], ]; /** * 返回的提示 * @return string[] */ public function messages() { return [ // 'name.required' => ':attribute字段不能为空', ]; } /** * 字段的明明包 可以把上面的:attribute 替换 * @return string[] */ public function attributes() { return [ // 'name' => '姓名', ]; } /** * 准备验证数据。验证前数据处理 * 比如把user_id 从header中取出来放进去 */ protected function prepareForValidation(): void { // var_dump("准备验证数据。验证前数据处理"); // $this->merge([ // 'slug' => Str::slug($this->slug), // 'user_id' => Auth()->user['id'] // ]); } /** * 配置验证实例。- 验证后数据处理前 可以添加错误信息- * - - - 上面的规则总有一些是 不满足使用的 * somethingElseIsInvalid 并不存在这个方法 就是告诉你这里可以有一些错误 * @param Validator $validator * @return void */ public function withValidator(Validator $validator): void { $validator->after(function (Validator $validator) { // if ($this->somethingElseIsInvalid()) { // $validator->errors()->add('field', 'Something is wrong with this field!'); // } }); } /** * 验证后数据处理 * 有些数据是不想写入数据库的 比如 _token 等 就需要去掉 * 如果删除了这个 仔细看父的实现- 返回$scenes内定义的参数,没定义的参数完全不返回 * @return void */ protected function passedValidation(): void { // var_dump("验证后数据处理"); // $this->replace(['name' => 'Taylor']); } } ``` #### 资源解释器-Resource ```php # 直接在toArray方法中重新编辑返回值 # $this 指的是 单条的Model 关联关系可以直接$this->关系 来查找 # 最后return 的是最终显示的值 namespace App\Http\Resources\api\common; use Illuminate\Http\Request; use Stevema\Restful\RestfulResource; class MakeTestResource extends RestfulResource { public function toArray(Request $request) { // return [ // "id" => $this->id, // "field1" => $this->field1, // ]; return parent::toArray($request); } } ``` #### 权限验证-Permission ```php # 权限验证只有俩个方法 namespace App\Http\Permissions\api\common; use Stevema\Restful\RestfulPermission; class MakeTestPermission extends RestfulPermission { /** * 权限验证 * 可以通过 $action = request()->route()->getActionMethod();获取当前的action * 然后判断是不是可以给权限 * @return bool */ public static function hasPermission(): bool { $action = request()->route()->getActionMethod(); if($action == 'store'){ // 新增不给权限 - 所有人不能新增 return False; } return True; } /** * 获取currentModel后判断一下 有没有权限 * @param $currentModel * @return bool */ public static function hasObjectPermission($currentModel): bool { $action = request()->route()->getActionMethod(); if($action == 'show'){ //详情都给权限 - 修改 删除 等就需要权限了 return True; } if($currentModel->user_id == auth()->user()->id){ return True; } return False; } } ``` ### 命令行 ```php # 1.生成整套的restful文件 $ php artisan make:restful api/common/make_test -m # 其中 api/common/ 是命名空间 make_test 是想要的模型名称 # 参数解释 {name : 数据库表名称 比如 sm_test_t1} # {--i|illustrate : 生成带说明的文件} # {--m|migration : 生成数据迁移文件} # {--s|seed : 生成数据填充文件}'; # 执行后输出 INFO Migration [database/migrations/2023_08_17_041228_create_make_tests_table.php] created successfully. INFO model [app/Models/api/common/MakeTest.php] created successfully. INFO resource [app/Http/Resources/api/common/MakeTestResource.php] created successfully. INFO request [app/Http/Requests/api/common/MakeTestRequest.php] created successfully. INFO filter [app/Http/Filters/api/common/MakeTestFilter.php] created successfully. INFO permission [app/Http/Filters/api/common/MakeTestPermission.php] created successfully. INFO controler [app/Http/Controllers/api/common/MakeTestController.php] created successfully. # 这个时候已经生成了 数据库迁移文件(Migration) 模型(Model) 解释器(Resource) 验证器(Request) 过滤器(Filter) 权限验证(Permission) 控制器(Controller) # 2.生成controller文件 $ php artisan make:restcontroller api/common/MakeTestController # 参数解释 {name : 控制器名称} # {--i|illustrate : 生成带说明的文件} # {--f|filter= : 过滤器::class} # {--m|model= : 模型::class} # {--r|resource= : 解释器::class} # {--R|request= : 表单验证::class} # {--p|permission= : 权限::class}'; # 3.生成filter文件 $ php artisan make:restfilter api/common/MakeTestFilter # 参数解释 {name : 过滤器名称} # {--i|illustrate : 生成带说明的文件} # {--r|resource= : 分页解释器[解释器::class]}'; # 4.生成filter文件 $ php artisan make:restmodel api/common/MakeTestModel # 参数解释 {name : 模型名称} # {--i|illustrate : 生成带说明的文件但是没什么用} # {--t|table= : 表名}'; # 5.生成permission文件 $ php artisan make:restpermission api/common/MakeTestPermission # 参数解释 {name : 权限名称} # {--i|illustrate : 生成带说明的文件但是没什么用}; # 6.生成request文件 $ php artisan make:restrequest api/common/MakeTestRequest # 参数解释 {name : 过滤器名称} # {--i|illustrate : 生成带说明的文件但是没什么用}; # 7.生成resource文件 $ php artisan make:restresource api/common/MakeTestResource # 参数解释 {name : 解释器名称} # {--i|illustrate : 生成带说明的文件但是没什么用}; # 8.生成cache文件 $ php artisan make:restcache api/common/MakeTestCache # 参数解释 {name : 缓存文件名} # {--i|illustrate : 生成带说明的文件但是没什么用}; ``` ### 备注 已添加permssion(权限验证) - 不是所有的页面都没有权限 主要是request里面的那个authorize 不是很好用 1、还不是很完善 - 下一版本会想办法加上缓存机制 - 已添加 - 目前只有 getOne setOne geetList setList flush forgetOne 2、测试里面的那些仅供参考-很多都是旧版本的东西了-跑不起来是正常事 3、删除了 Traits/UseTableNameAsMorphClass 因为我新写了 laravel-morphmap 功能更全 链接:https://packagist.org/packages/stevema/laravel-morphmap