bfg/resource

为Laravel添加的小功能

维护者

详细信息

github.com/bfg-s/resource

源代码

问题

安装: 25 136

依赖: 1

建议者: 0

安全: 0

星标: 1

关注者: 2

分支: 1

开放问题: 0

类型:bfg-app

3.2.7 2024-07-26 20:36 UTC

README

安装

composer require bfg/resource

描述

为Laravel添加的小功能,由于Laravel主团队对新的Stub的重写,该功能是哑的并可修改。它包括一个包含资源继承的新Stub,反过来,它也继承自Illuminate\Http\Resources\Json\JsonResource,因此Laravel会将其视为一个完整的资源。

关于概念。

扩展的概念是减少资源并使其更加灵活和通用。灵活性在于每个字段都有独立的数据获取可能性,并使用Laravel Eloquent CastsLaravel 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 FortifyJetStream作为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\EloquentScopesTraitBfg\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-username-field-user字段。

...
    #[CanFields([
        'id', 'name' => 'my-policy'
    ])] protected array $map = [
        'id',
        'name',
    ];
...

将检查id-field-usermy-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
...

木材支持

wood.veskod.com