marcot89 / laravel-bullet
Laravel应用程序的子弹开发
Requires
- php: ^7.2
- illuminate/database: ^6.0
- illuminate/http: ^6.0
- illuminate/support: ^6.0
- orchestra/testbench: ^4.0
Requires (Dev)
- phpunit/phpunit: ^8.3
README
Laravel Bullet
⚡️ 为Laravel应用程序提供闪电般的CRUD和路由注册
此包让您能够快速地将API Cruds转换为Eloquent资源,您可以使用其基于约定的动态路由注册。如果您不喜欢脚手架,不喜欢对资源进行重复的CRUD操作和路由注册,那么这个包就是您和您的应用程序的理想选择。
目录
安装
您可以通过composer安装此包
composer require marcot89/laravel-bullet
推荐: 此包推荐使用来自 Spatie 团队的 Laravel Query Builder 为索引操作。
基本用法
只需在您的控制器类中扩展 ResourceController
<?php namespace App\Http\Controllers; use MarcoT89\Bullet\Controllers\ResourceController; class UserController extends ResourceController { }
完成!现在您已经拥有了控制器类中模型 User
的所有这些CRUD操作
- index
- store
- update
- destroy
- show
- edit
- forceDelete*
- restore*
重要: 方法
forceDelete
和restore
仅在资源方法使用Laravel的traitSoftDeletes
时显示。
约定: 资源模型是根据约定推断的。如果您有一个名为
PostController
的控制器,它将推断模型Post
。控制器名称的约定如下:[Model]Controller
,它 不会 从复数到单数进行解析。因此,如果您定义一个控制器PostsController
,它将尝试解析模型Posts
而不是Post
。在创建控制器时请记住这一点。
到此为止!这足以向您的控制器添加CRUD操作。但 如何动态定义路由呢? 我们接下来将要看到的。
动态路由
现在您已经创建了一个 UserController
,是时候为资源控制器注册路由了,对吗?但如果这些路由可以自动注册怎么办?好的!让我们来做吧!
在您想使用的任何路由组中使用 Bullet::namespace
。例如
// routes/web.php Route::middleware('auth', function () { Bullet::namespace('Resources'); // the default namespace is App\Http\Controllers });
就这样!现在 所有 在 Resources
命名空间下创建的控制器都将自动注册其公共方法。以下是以下路由
如果您想隐藏某些特定的路由或操作,可以使用控制器保护属性 $only
或 $except
来实现
class UserController extends ResourceController { protected $only = ['index', 'show']; // or protected $except = ['destroy']; }
这将仅生成预期的路由。
注意: 请记住,
$only
属性优先于$except
属性,并且不能同时使用。
重要:
$only
属性将隐藏所有其他操作,包括动态操作。
中间件配置
因为我们使用动态路由,中间件配置是在控制器属性中设置的。以下是一些示例
// You can set only one middleware for all actions protected $middleware = 'auth'; // or many middlewares protected $middleware = ['auth', 'verified']; // or customized for different actions protected $middleware = [ 'auth', 'auth:api' => ['except' => 'index'], 'verified' => ['only' => ['store', 'update', 'destroy']] ];
策略类
如果遵循约定,策略类会自动使用。例如,如果你的控制器模型是User
,laravel-bullet将自动尝试注册UserPolicy
策略类。但如果策略类不存在,它将跳过授权。
如果你想为控制器定制策略类,你可以在控制器中设置属性$policy
。如下所示
class UserController extends ResourceController { protected $policy = \App\Policies\CustomUserPolicy::class; }
如果你不想将策略类注册到控制器,即使它存在,只需将$policy
属性设置为false
。
提示:策略类会自动注册,你不需要在
AuthServiceProvider
中注册它。
验证和请求
建议在请求类中使用验证。请求会自动通过约定注入。如果你在控制器中有模型User
,并且当前动作是store
,laravel-bullet将尝试按照以下约定顺序注入请求类
App\Http\Requests\Users\StoreRequest::class App\Http\Requests\UserStoreRequest::class
如果上述类都不存在,它将注入默认的laravel Illuminate\Http\Request
类。
如果你想为特定动作自定义请求类,你可以在控制器中的受保护属性requests
中定义它
use App\Http\Requests\MyCustomUserRequest; class UserController extends ResourceController { protected $requests = [ 'store' => MyCustomUserRequest::class, ]; }
现在,请求MyCustomUserRequest
类将被注入到store动作中。
老式验证
如果你不喜欢请求类中的验证,或者更喜欢laravel的validate
方法,你可以在动作钩子中使用它
class UserController extends ResourceController { protected function beforeStore($request, $attributes) { return $this->validate($request, [ 'name' => 'required', 'email' => 'required|email', 'password' => 'required|min:6|confirmed', ]); } }
你可以在下一个主题中找到更多关于动作钩子的信息。
操作钩子
在动作成功或失败时执行一些操作是非常常见的。例如,触发事件、记录日志、调度作业等。所有CRUD动作都有before
和after
钩子动作。
它们非常有用,例如,为用户加密密码
class UserController extends ResourceController { protected function beforeStore($request, $attributes): array { $attributes['password'] = bcrypt($attributes['password']); return $attributes; } }
或者在一个用户创建时触发一个事件
use App\Events\UserCreated; class UserController extends ResourceController { protected function afterStore($request, $user) { event(new UserCreated($user)); } }
以下是每个动作的声明式动作钩子列表
// Hooks for index action protected function beforeIndex($request); protected function afterIndex($request, $builder); // Hooks for store action protected function beforeStore($request, $attributes): array; // should return the attributes for the model being stored. protected function afterStore($request, $model); // Hooks for update action protected function beforeUpdate($request, $attributes, $model): array; protected function afterUpdate($request, $model); // Hooks for destroy action protected function beforeDestroy($request, $model); protected function afterDestroy($request, $model); // Hooks for show action protected function beforeShow($request, $model); protected function afterShow($request, $model); // Hooks for edit action protected function beforeEdit($request, $model); protected function afterEdit($request, $model); // Hooks for forceDelete action protected function beforeForceDelete($request, $model); protected function afterForceDelete($request, $model); // Hooks for restore action protected function beforeRestore($request, $model); protected function afterRestore($request, $model);
API资源(表现层)
要设置自己的API资源,你可以在控制器类中使用受保护的resource
属性。例如
use App\Http\Resources\UserResource; class UserControlle extends ResourceController { protected $resource = UserResource::class; }
注意:此资源将在所有动作中使用。
详细操作
分页、过滤器和其他魔法
如果你使用推荐的Laravel Query Builder composer包,你应该能够很容易地使用它的所有功能。在下面的示例之前,请阅读它们的文档并熟悉它们的用法。
所有这些属性都对index
和show
动作可用
protected $defaultSorts = null; protected $allowedFilters = null; protected $allowedIncludes = null; protected $allowedSorts = null; protected $allowedFields = null; protected $allowedAppends = null; protected $defaultPerPage = 15; protected $maxPerPage = 500; protected $searchable = true;
警告:这组属性仅与Laravel Query Builder包一起使用。如果没有它,将不会工作。
看看它是如何工作的
$defaultSorts
// Sort your records from latest to oldest by default protected $defaultSorts = '-created_at';
$allowedFilters
protected $allowedFilters = ['name', 'email'];
或者,你可以使用allowedFilters
方法来处理更复杂的情况
protected function allowedFilters() { return [ 'name', AllowedFilter::exact('id'), AllowedFilter::exact('email')->ignore(null), AllowedFilter::scope('with_trashed'), AllowedFilter::custom('permission', FilterUserPermission::class), ]; }
$allowedIncludes
protected $allowedIncludes = ['posts.comments'];
或者,你可以使用allowedIncludes
方法来处理更复杂的情况
protected function allowedIncludes() { $includes = ['posts.comments']; if (user()->isAdmin()) { $includes[] = 'created_by'; $includes[] = 'logs'; } return $includes; }
$allowedSorts
protected $allowedSorts = ['id', 'name', 'created_at'];
或者,你可以使用allowedSorts
方法来处理更复杂的情况
protected function allowedSorts() { return [ 'id', 'name', 'created_at', Sort::field('street', 'actual_column_street'), ]; }
$allowedFields
protected $allowedFields = ['id', 'name', 'email'];
或者,你可以使用allowedFields
方法来处理更复杂的情况
protected function allowedFields() { return [ 'id', 'name', 'email', ]; }
$allowedAppends
protected $allowedAppends = ['is_admin', 'is_published'];
或者,你可以使用allowedAppends
方法来处理更复杂的情况
protected function allowedAppends() { return ['is_admin', 'is_published']; }
$defaultPerPage
protected $defaultPerPage = 15; // defaults to 15
这可以通过per_page
或perPage
查询参数传递。
$maxPerPage
protected $maxPerPage = 500; // defaults to 500
但是,per_page
或 perPage
参数不能超过这个 $maxPerPage
限制。如果你传递 per_page=1000
,并且已经定义了它,分页将限制为300。这是对查询的保护。请明智地使用它。
$searchable
protected $searchable = true; // defaults to true
默认情况下,所有索引操作都接受 search
查询参数,如果存在,它将尝试使用模型中的 scopeSearch
范围。如果不存在,它将忽略它。如果你在模型中有搜索范围,但不希望它在索引操作中可用,请将其设置为 false。
操作的自定义查询构建器
在很多情况下,你有一个针对查询的大范围。例如,如果你正在开发一个具有 teams
的多租户应用程序,有时你可能想要列出当前用户的团队中的用户。在这种情况下,你可以自定义控制器的查询。目前,每个操作都有自己的查询方法。
对于索引操作,你应该重写 getQuery
方法,如下所示
protected function getQuery() { return team()->users(); }
以下是查询构建器需要覆盖的完整操作列表
protected function getQuery($request); // for index action protected function getStoreQuery($request); protected function getUpdateQuery($request); protected function getDestroyQuery($request); protected function getShowQuery($request); protected function getEditQuery($request); protected function getForceDeleteQuery($request); protected function getRestoreQuery($request);
自定义操作
要完全自定义 CRUD 操作,只需按照预期声明它。由于限制原因,所有需要 $id
来查找模型的路由都使用 $id
参数而不是类型化模型对象。由于其声明,不可能注入请求类。例如,如果你想要自定义接收 $id
的更新方法,你应该这样做
public function update($id) { $user = User::findOrFail($id); ... }
现在你可以使用动作方法做任何事情。
高级路由
当使用子弹动态路由时,你不需要在任何一个路由文件中手动编写任何路由。使用子弹路由,控制器中的任何公共方法都成为具有已注册路由的操作。示例
class UserController extends ResourceController { public function reports() { ... } }
公共 方法将 自动 注册一个路由,如下所示
Route::get('users/reports', 'Resources\UserController@reports')->name('users.reports');
自定义HTTP方法
如果你想要自定义路由的 HTTP 方法,只需用该 HTTP 方法的名称作为前缀。如下所示
public function postReports() { ... }
现在,公共方法 postReports
将注册一个具有 POST HTTP 方法的路由
Route::post('users/reports', 'Resources\UserController@postReports')->name('users.reports');
提示:请注意,生成的 路由名称 是
'users.reports'
而不是'users.post-reports'
。
路由参数和依赖注入
你还可以向操作传递参数,它们将被转换为 URL 参数。
use App\Models\User; use App\Http\Requests\MyCustomRequest; class UserController extends ResourceController { public function postReports(MyCustomRequest $request, User $user, $report, $param1, $param2) // as many as you want. { ... } }
现在这个公共方法将注册一个路由,如下所示
Route::post('users/reports/{user}/{report}/{param1}/{param2}', 'Resources\UserController@postReports')->name('users.post-reports');
注意:任何类型化的参数都将按照预期正常注入。请求参数将注入,但在路由定义中将被忽略。
从动态路由中排除控制器
有时你必须为你的控制器之一定义非常自定义的路由。为了排除你的控制器从动态路由中,你应该在 namespace
方法中使用 options
参数。如下所示
Bullet::namespace('Api/V1', ['except' => 'InternalController']);
现在这个控制器的路由将不会自动生成。你必须 手动 注册它的路由。
性能和其他提示
仅对操作使用公共方法
由于公共方法会自动注册路由,因此将其他辅助方法使用 protected
或 private
可见性是一种好习惯,以避免生成 垃圾路由。
使用 route:cache
命令提高性能
由于 laravel-bullet 为每个控制器类使用了大量的反射和IO,因此对于生产环境,使用 route:cache
命令将显著提高你的请求性能。
HTML和JSON响应
index
动作在 AJAX 请求中响应 JSON,或者将尝试在 resources/views/users/index.blade.php
下返回视图。
变更日志
请参阅 CHANGELOG 了解最近更改的详细信息。
贡献
请参阅 CONTRIBUTING 了解详细信息。
安全
如果您发现任何与安全相关的问题,请通过电子邮件发送至 marcotulio.avila@gmail.com,而不是使用问题跟踪器。
鸣谢
许可
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。
Laravel软件包模板
本软件包是使用Laravel Package Boilerplate生成的。