bfg / resource
为Laravel添加的小功能
Requires
- php: >=8.0.0
- bfg/embedded-call: *
This package is auto-updated.
Last update: 2024-09-26 20:56:58 UTC
README
安装
composer require bfg/resource
描述
为Laravel添加的小功能,由于Laravel主团队对新的Stub的重写,该功能是哑的并可修改。它包括一个包含资源继承的新Stub,反过来,它也继承自Illuminate\Http\Resources\Json\JsonResource,因此Laravel会将其视为一个完整的资源。
关于概念。
扩展的概念是减少资源并使其更加灵活和通用。灵活性在于每个字段都有独立的数据获取可能性,并使用Laravel Eloquent Casts和Laravel Eloquent Mutators。此外,还能确定模型之间的关系并在加载时连接字段。分页规则。
可能性
此包与bfg/route包结合使用时,可以组织一个不太大但非常强大的API模式,其便利性可以与GraphQL相媲美。
在哪里使用它?
此包旨在仅作为Laravel框架的附加组件。使用该包的完整功能,可以快速有效地实现健壮的API资源,不仅限于模型。
创建新资源
要创建资源,只需使用标准命令即可
php artisan make:resource user
我在那里添加了一些属性
-m, --model[=MODEL] Create with the model
-r, --route Create with route
-c, --collection Create a resource collection
执行后,将创建app/Http/Resources/UserResource.php文件,内容如下
<?php namespace App\Http\Resources; use App\Models\User; use Bfg\Resource\BfgResource; use Bfg\Resource\Traits\ModelScopesTrait; use Bfg\Resource\Traits\EloquentScopesTrait; /** * @mixin User */ class UserResource extends BfgResource { use EloquentScopesTrait, ModelScopesTrait; /** * Map of resource fields * @var array */ protected array $map = [ ]; }
接下来,我们需要填写资源映射。
... protected array $map = [ 'id', 'name', 'email', 'phone', 'photo', // Variants 'avatar' => AvatarResource::class, // Use resource 'avatar' => [AvatarResource::class, 'photo'], // Path to field 'avatar' => [AvatarResource::class, 'photo.src'], // You can use a dot path, for relations an example 'avatar' => ['photo.src'], // Or just set a new path for field in array 'avatar' => 'photo.src', // Or in string ]; ...
转换器
例如,对于用户,我们需要对其照片字段进行数据处理,为此我们可以使用转换器。所有转换器都遵循与Laravel模型相同的规则,只是将Attribute替换为Field。
... public function getPhotoField ($value) { return $value ? asset($value) : asset('images/default_photo.jpg'); } ...
转换
所有资源转换规则完全复制自Laravel属性的转换,其功能完全相同(除了自定义转换的set之外)。
... protected array $casts = [ 'id' => 'int' ]; ...
扩展(重用)
组合资源字段的结果。在主要资源重新定义父级之前执行。
重要!通用资源应与一个模型一起使用。
... protected array $extends = [ UserResource::class => ['id', 'name'], // Insert only id and name fields UserDetailsResource::class => 'phone', // Insert only phone field UserCommentResource::class, // Inserted all fields ]; ...
路由
要使用资源作为API控制器,建议使用Laravel Fortify或JetStream作为API提供者。
路由定义
开始之前,您需要安装包bfg/route
composer require bfg/route
在您的RouteServiceProvider中添加路由搜索指针
... /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot() { $this->configureRateLimiting(); $this->routes(function () { ... Route::find( __DIR__.'/../Resources', Route::prefix('api') ->middleware(['api', 'auth:sanctum']) ->as('api.') ); //Route::prefix('api') // ->middleware('api') // ->namespace($this->namespace) // ->group(base_path('routes/api.php')); ... }); } ...
然后您需要向资源类添加一个属性
... use Bfg\Resource\Attributes\GetResource; /** * @mixin User */ #[GetResource] class UserResource extends BfgResource { ... }
之后,您将看到路由链接api/user/{scope},它引用此资源。
路由中的范围
所有资源范围应该是静态和公开的。通过将scopes的参数链接起来,必须理解它们是如何工作的。范围调用顺序的思路如下:通过查询引用中的斜线顺序固定,例如
[GET] http://example.com/api/user/get/only/...
在结果中,您将收到一个不带任何字段的用户列表,因为Only参数只接受需要显示的字段。为了在scope中传输字段,我们使用与顺序调用相同的语法,只有在每个scope传递其参数之后,资源控制器才会按顺序处理字符串,如果在此序列中找到scope,则需要执行。如果下一个(后续)scope的名称不出现在这些名称中,则所有其他参数都视为scope的参数,依此类推。
这是获取具有分页和字段过滤的数据请求的方式
重要!您的资源必须连接到特质
Bfg\Resource\Traits\EloquentScopesTrait和Bfg\Resource\Traits\ModelScopesTrait
[GET] http://example.com/api/user/paginate/15/only/id/name [GET] http://example.com/api/user/[:paginate]/[perPage]/[:only]/[field]/[field]
每个scope在每个预请求中都是唯一的,在scope
分页后,集合也会返回,在这种情况下,它的每个记录都会通过only范围进行处理。
在onlyscope之后未进入资源的字段将用null填充
在资源映射中的字段名前放置一个问号?
... protected array $map = [ '?id', // <-- ]; ...
在资源中填入一个数组,包含必须临时显示的字段
... protected array $temporal = [ 'id', // <-- ]; ...
将触发器设置为true,这将使所有资源字段都临时可用
... protected bool $temporal_all = true; ...
资源中的范围
在Scopes资源的类中,看起来像是具有CamelCase命名和后缀Scope的公共静态函数
考虑标准的Resource Eloquent Get Scope
... public static function getScope($model): mixed { return $model->get(); } ...
Scope的第一个参数是当前模型,它可能来自上一个scope,或者是从标准资源模型中来的,这将根据资源名称自动尝试确定,或者您必须在BfgResource::$model参数中指定模型或覆盖public static function getResource(): mixed。
现在考虑only scope,我们按顺序接受所有参数
... public static function onlyScope($model, ...$fields): mixed { return $model?->only($fields); } ...
... public static function paginateScope( $model, int $perPage = null, string $pageName = 'page', int $page = null, ...$columns ): \Illuminate\Contracts\Pagination\LengthAwarePaginator { /** @var Model $model */ return $model->paginate($perPage, $columns ?: ['*'], $pageName, $page); } ...
因此,我们首先获取整个列表,然后逐个,并通过PHP方式进行参数验证。
相同的Rout资源采用各种查询方法,对于这些方法,我们有专门的scope。我们正式地将方法名称添加到updatePostScope函数的名称中——对于post
默认范围
为了方便起见,集合中已经准备好了现成的scope,这可以简化您的开发。
EloquentAllScopeTrait
添加接收所有记录的scope特质。
[GET] http://example.com/api/user/all [GET] http://example.com/api/user/[all]
EloquentFindScopeTrait
添加通过id搜索的scope特质。
[GET] http://example.com/api/user/find/1 [GET] http://example.com/api/user/[find]/[id]
EloquentFirstScopeTrait
添加获取第一条记录的scope特质。
[GET] http://example.com/api/user/first [GET] http://example.com/api/user/[first]
EloquentForPageScopeTrait
添加获取页面分页数据的scope特质。
[GET] http://example.com/api/user/for_page/2 [GET] http://example.com/api/user/[for_page]/[page]/[perPage=15]
EloquentLatestScopeTrait
添加获取最新行的scope特质。
[GET] http://example.com/api/user/latest [GET] http://example.com/api/user/[latest]/[column=id]
EloquentLimitScopeTrait
添加获取指定数量数据的scope特质。
[GET] http://example.com/api/user/limit/3 [GET] http://example.com/api/user/[limit]/[count]
EloquentOrderByScopeTrait
添加获取排序数据的scope特质。
[GET] http://example.com/api/user/order_by/id [GET] http://example.com/api/user/[order_by]/[column]/[direction=asc]
[GET] http://example.com/api/user/order_by_desc/id [GET] http://example.com/api/user/[order_by_desc]/[column]
EloquentPaginateScopeTrait
添加接收分页记录的scope特质。
[GET] http://example.com/api/user/paginate [GET] http://example.com/api/user/[paginate]/[perPage=null]/[pageName=page]/[page=null]
EloquentRandomScopeTrait
添加获取随机数据的scope特质。
[GET] http://example.com/api/user/random [GET] http://example.com/api/user/[random]/[seed=]
EloquentSkipScopeTrait
添加跳过ID记录的scope特质。
[GET] http://example.com/api/user/skip [GET] http://example.com/api/user/[skip]/[ids]...
EloquentWhereScopeTrait
添加请求条件的scope特质。
[GET] http://example.com/api/user/where/phone/3800000000 [GET] http://example.com/api/user/[where]/[column]/[condition]/[value=null]
EloquentWithScopeTrait
添加加载资源关系的scope特质。
[GET] http://example.com/api/user/with/commentaries-images/profile [GET] http://example.com/api/user/[with]/[relation->relation]/[relation->relation]...
通过-在所有关系中进行深度下载。
ModelOnlyScopeTrait
添加字段限制的scope特质,它在样本之后工作。
[GET] http://example.com/api/user/first/only/id [GET] http://example.com/api/user/[first]/[only]/[field]/[field]...
EloquentScopesTrait
连接所有Eloquent scope的通用特质。
ModelScopesTrait
所有Model scope的通用特性。
查询方法的最终状态。
对于GET方法,标准的结束scope All生效。对于OPTIONS方法,使用scope first。
策略
为了使用Laravel Policy进行保护,我添加了负责此功能的属性。
CanResource
检查resource的属性。
#[GetResource, CanResource] class DirectorResource extends BfgResource { ... }
将检查id-field-user字段。
CanScope
检查scope的属性。
use Bfg\Resource\Attributes\CanScope; ... #[CanScope] public static function myScope($model, array $data, int $id): mixed { return $model; } ...
由于这是一个条件资源UserResource,将更多通过策略my-user进行检查,或者您可以指定自己的#[CanScope('my-policy')]。
CanFields
检查字段或字段的属性。它仅适用于map参数。
... #[CanFields([ 'id', 'name' ])] protected array $map = [ 'id', 'name', ]; // Or #[CanFields('id', 'name')] protected array $map = [ 'id', 'name', ]; ...
将检查id-field-user和name-field-user字段。
... #[CanFields([ 'id', 'name' => 'my-policy' ])] protected array $map = [ 'id', 'name', ]; ...
将检查id-field-user和my-policy。
如果策略字段不匹配,它将简单地不在整体列表中存在。
CanUser
检查资源字段与用户字段。
#[GetResource, CanUser] class DirectorResource extends BfgResource { ... }
将检查resource->user_id == auth->user->id。
或者您可以手动指定要检查的字段。
#[GetResource, CanUser('local_field', 'user_field')] class DirectorResource extends BfgResource { ... }
将检查resource->local_field == auth->user->user_field。
如果策略不匹配,它将简单地不在整体列表中存在,但保留在集合中。
PHP API
创建一个实例
UserResource::make(User::first());
创建实例集合
UserResource::collection(User::get());
自动检测并创建集合或单个资源实例的方法
UserResource::create(User::first()): static; // or UserResource::create(User::get()): BfgResourceCollection;
在辅助函数中
isParent()- 是否是根嵌套级别。isChild()- 是否是子嵌套级别。isNesting(int $needleNested)- 是否是嵌套级别等于目标嵌套级别。isCollected()- 如果处理的是此资源的集合挑战。nesting()- 获取嵌套级别。
PHP类范围API
在PHP中调用scope,您可以使用静态构造函数
use App\Http\Resources\UserResource; ... // For get resource instance UserResource::scope('where', 'name', 'admin', 'first'); // For get resource array result UserResource::scope('where', 'name', 'admin', 'first')->toFields(); ...
您可以获取对象以供使用
use App\Http\Resources\UserResource; ... // For get resource array result UserResource::use('where', 'name', 'admin', 'first'); // object with data ...