stevema/laravel-restful

laravel 中 每个restful接口都需要写一堆方法。我想写一个公共的类 然后继承它 只需要少少的配置就可以输出相关的API接口

v1.0.0 2023-11-24 08:43 UTC

This package is not auto-updated.

Last update: 2025-01-03 14:05:35 UTC


README

介绍

laravel 中 每个restful接口都需要写一堆方法。我想写一个公共的类 然后继承它 只需要少少的配置就可以输出相关的API接口

安装教程

# 安装
$ composer require stevema/laravel-restful

使用说明

# 比如我现在想加个配置相关的表并输出对应的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

#对于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-主要是软删除

# 配置模型(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

# 过滤器目前只给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

# 验证器和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

# 直接在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

# 权限验证只有俩个方法

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;
    }
}

命令行

# 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