phpsa/laravel-api-controller

一个基于Laravel的API基本控制器,将基本的CRUD映射到模型

v7.3.2 2024-08-08 20:53 UTC

This package is auto-updated.

Last update: 2024-09-08 21:01:32 UTC


README

[[目录]]

For Laravel 9 to 10 Build Status Coverage Status Packagist Packagist Packagist Github Issues

默认可扩展的用于模型的基本CRUD API方法包括列表、显示、更新、添加和删除端点,用于与模型交互。

安装

通过composer安装

composer require phpsa/laravel-api-controller

发布配置文件(可选 - 如果您需要更改任何默认配置)

php artisan vendor:publish --provider="Phpsa\LaravelApiController\ServiceProvider" --tag="config"

使用方法

CLI命令

  • artisan make:api:controller {ControllerName} 生成控制器
  • artisan make:api:policy {PolicyName} -m {Model} 生成策略文件
  • artisan make:api:resource {ResourceName|CollectionName} 生成响应资源

这将为您创建一个Api/ModelNameController,并且您将具备以下基本路由

  • GET api/v1/{model_name} - 列出所有/分页/过滤(类::index)
  • GET api/v1/{model_name}/$id - 显示指定ID(类::show)
  • POST api/v1/{model_name} - 插入新记录(类::store)
  • PUT api/v1/{model_name}/$id - 替换现有记录(类::update)
  • PATCH api/v1/{model_name}/$id - 更新现有记录(类::update)
  • DELETE api/v1/{model_name}/$id - 删除现有记录(类::destroy)

如果您在make:api:controller上指定了--soft-deletes选项,它还会创建一个额外的restore控制器端点和路由

  • PATCH api/v1/{model_name}/$id - 恢复软删除记录(类::restore)。这仅适用于启用了软删除的模型。

您可以通过简单地添加自己的方法来覆盖方法 - 上面的花括号中的方法名称

事件

  • POST(类::store)- 触发一个新的Phpsa\LaravelApiController\Events\Created事件,该事件将新记录作为$record提供
  • PUT(类::update)- 触发一个新的Phpsa\LaravelApiController\Events\Updated事件,该事件将更新的记录作为$record提供
  • DELETE(类::destroy)- 触发一个新的Phpsa\LaravelApiController\Events\Deleted事件,该事件将删除的记录作为$record提供

策略

策略:[https://laravel.net.cn/docs/6.x/authorization#generating-policies](https://laravel.net.cn/docs/6.x/authorization#generating-policies)

使用php artisan make:policy PostPolicy --model=Post生成

  • 获取列表 - 调用viewAny策略
  • 获取单个 - 调用view策略
  • 发布新内容 - 调用create策略
  • 更新 - 调用update策略
  • 删除项目 - 调用delete策略

策略中的查询/数据修改器用于API端点

  • qualifyCollectionQueryWithUser($user, $repository) -> return void - 向存储库添加任何查询(例如 ->where('x',''))
  • qualifyItemQueryWithUser($user, $repository)-> return void - 向存储库添加任何查询(例如 ->where('x',''))
  • qualifyStoreDataWithUser($data) - 返回更新的数据数组
  • qualifyUpdateDataWithUser($data) - 返回更新的数据数组

资源/集合(转换)

资源:[https://laravel.net.cn/docs/6.x/eloquent-resources](https://laravel.net.cn/docs/6.x/eloquent-resources)

使用php artisan make:apiresource UserResourcephp artisan make:api:resource UserCollection生成

更改资源以扩展

使用 Phpsa\LaravelApiController\Http\Resources\ApiResource 作为你的资源,使用 Phpsa\LaravelApiController\Http\Resources\ApiCollection 作为你的资源集合

在你的控制器中覆盖以下参数

	protected $resourceSingle = UserResource::class;
	protected $resourceCollection = UserCollection::class;

蛇形命名法 vs 驼峰命名法

  • 将所有驼峰命名法转换为蛇形命名法的中间件: Phpsa\LaravelApiController\Http\Middleware\SnakeCaseInputs
  • 设置请求头 X-Accept-Case-Typesnakecamel 以更改你的数据响应

过滤

稳定的选项,一旦实验稳定后将删除

对于 get 命令,你可以使用以下 URL 模式进行过滤

包含 / 不包含

你可以向过滤器传递一个值数组,例如: filter[user_id]=1||2||||4||7filter[user_id!]=55||33

空 / 非空(从 1.23.0 版本引入)

如果你需要过滤字段是否为空或非空,可以使用自 1.23.0 版本以来的过滤器参数。例如: filter[age]=NULLfilter[age!]=NULL。注意,NULL 必须为大写。

旧版本 在你的模型中添加一个作用域:例如

public function scopeAgeNull(Builder $builder, $isNull = true){
  $isNull ? $builder->whereNull('age') : $builder->whereNotNull('age');
}

添加到你的 allowedScopes,然后在 URL 中调用它,例如 ?ageNull=1 用于 where null 和 ?ageNull=0 用于 where age not null

过滤

  • 使用 URL 模式 filters[column][operator]=value 例如 filters[age][>]=18&filters[title][contains]=testing
  • 空 = filters[age]=NULL 将生成 where age is null

  • JSON 列 = filters[meta->seo][is]=enabled 将生成

where json_unquote(json_extract(`meta\`, '$."seo"')) = 'enabled'
  • 关系: filters[relationName][has]filters[relationName][!has]filters[relation_name][not_has]
  • 关系过滤 filters[tags][has][slug]=my_slug
  • 关系 filters[tags]=truefilters['tags.slug']=myslug filters[tags.slug][!]=myslug filters[tags.slug][!][contains]=money

在控制器上强制作用域/查询过滤器覆盖

protected function getNewQuery(): Builder
    {
        return resolve($this->model())->newQuery();
    }

方法在你的控制器中包含任何额外的查询/作用域等。

请求

我们添加了一个请求宏,以便您可以根据需要设置这些内容

例如

    public function index(Request $request)
    {

        $request->apiFilter('user_id', auth()->id());
        $request->apiFilter('owner_id', 'not_equals', auth()->id());
        $request->apiFilter('age', '>=', 5);
        $request->apiFilter('age', '<=', 10);
        $request->apiInclude(['owner','user']);
        $request->apiAddFields(['x','y','z']);
  • 过滤方法为 apiFilter($column, $value)apiFilter($column, $operator, $value)
  • 添加字段方法为 apiAddFields(array|string $fieldsOrAttributesToInclude)
  • 包含方法为 apiInclude(array|string $relations)

在你的控制器中,我们通常使用 request->all() 来填充模型。如果你希望使用更严格的选项,可以选择仅在控制器中调用 $this->setOnlyValidated() 来仅使用验证过的值,这将使用 request->validated() 来获取数据(注意:这意味着它将不会从 postValidation 中获取任何合并的信息)。

作用域

除了过滤之外,您还可以使用 Laravel 的 Eloquent 查询作用域 来执行更复杂的搜索或过滤。只需将 $allowedScopes 添加到您的 ApiResource 中,该作用域就会作为查询参数公开。

假设你在你的 Eloquent 模型上定义了一个 scopeFullname,你可以按照以下方式将其公开到 API 中

protected static $allowedScopes = [
  'fullname'
];

给定上述 $allowedScopes 数组,你的 API 用户现在可以请求 ?fullname=John。查询参数值将传递到你的 Eloquent 模型的作用域函数中。

过滤相关模型

你可以轻松地使用配置了 include 的任何相关模型进行过滤。只需在查询字符串中指定 ?filter[model.field]=123 即可。相同的过滤选项也适用于相关字段。

分组过滤作用域

filter_by_relation_group[a][name]=weight&filter_by_relation_group[a][value][>]=900&filter_by_relation_group[b][name]=color&filter_by_relation_group[b][value]=color

 public function scopeFilterByRelationGroup(Builder $builder, array $wheres): void
    {
        $where =  collect($wheres)->map(fn ($child) =>
           $this->parseFiltersArray($child)
        )->each(
            fn($group, $key) => $builder->whereHas('Relation', function ($subQ) use ($group, $key) {
                $group->each(
                    fn($filter, $column) => collect($filter)->each(fn($value, $comparison) => $this->buildQuery($column, $comparison, $value, $subQ))
                );
            }
            )
        );

    }

字段、关系、排序 & 分页

字段

默认情况下返回所有字段,你可以通过以下方式限制到特定字段

  • API 控制器参数 $defaultFields 默认为 protected $defaultFields = ['*']; - 切换到包含字段数组
  • URL 查询字符串中的 fields 参数:例如 fields=id,name,age = 只返回这些,这将覆盖上述内容。
  • 在你的响应资源中,你可以设置 static::allowedFields 来锁定可返回的字段。
    • 这还控制了哪些相关资源可以被返回。包括在 $mapResources 中使用的键(见下文“关系”)。
  • URL查询字符串中的 addfieldsremovefields 参数将与这些一起工作。
  • 使用laravel eloquent模型 $appends 属性来自动包含自定义属性访问器。

门控响应字段

可以通过定义 $gatedFields 来使用门控控制对字段和相关资源的访问。

protected static array $fieldGates = [
    'gate-one' => [
        'fieldA',
        'fieldB',
     ],
     'gate-two' => [
       'fieldA',
       'fieldC,
       'relatedResourceD'
     ]
];

每个指定的门控都将用来确定是否包含那一组字段。

每个门控都会传递资源和用户,因此它可以测试用户是否应该被允许访问那个特定的资源。

示例门控定义

  Gate::define(
      'supervises-the-group',
      fn ($user, Group $group) => (int) $user->id === $group->supervisor_id
  );

关系

  • 使用在您的模型中定义的关系,您可以通过逗号分隔的列表传递,例如 include=join1,join2,这将返回那些连接(一个或多个)。

只需在您的 Resource 中添加一个 protected static $mapResources,以定义要分配相关数据的资源。例如,对于一对多关系,应指定一个集合,而对于一对一关系,则直接指定相关资源。这将允许API正确格式化相关记录。

    protected static $mapResources = [
        'notes' => NotesCollection::class,
        'owner' => OwnerResource::class
    ];
  • 您可以为大多数类型的关系自动更新和创建相关记录。只需在您的POST或PUT请求中包含相关资源名称即可。
  • 重要:如果您在资源中使用 $defaultFields 和/或 $allowedFields,则 $mapResources 中的相关资源键也必须包含在这些列表中,以便包含该相关资源。

对于 BelongsToManyMorphToMany 关系,您可以选择同步策略。默认情况下,这将采用 添加 策略。也就是说,发送的关联记录将被添加到任何现有的相关记录中。在每次请求的基础上,您可以选择 同步 策略,这将删除请求中未列出的任何相关记录的枢纽。注意实际的相关记录不会被删除,只是枢纽条目。

要选择 同步 行为,请将请求中的 ?sync[field]=true 设置为。

排序

  • 排序也可以作为逗号列表传递,例如 sort=age ascsort=age asc,name desc,eyes - 分别生成 sort age ascsort age asc, name desc, eyes asc 的SQL。
  • 默认排序也可以通过在控制器中重写 protected $defaultSort = null; 参数来添加。

分页

  • 可以通过在控制器中重写 protected $defaultLimit = 25; 来启用/禁用分页。
  • 分页也可以通过URL使用 limit=xx&page=y 来传递。
  • 分页也可以通过重写 protected $maximumLimit = false; 参数来限制每页的最大数量。

验证

  • 在发布新记录时,可以通过向控制器添加一个 rulesForCreate 方法来执行验证,该方法返回一个数组,例如
[
    'email' => 'required|email',
    'games' => 'required|numeric',
]

请参阅 https://laravel.net.cn/docs/5.8/validation#conditionally-adding-rules

  • 对于更新记录,按上述方法添加一个方法 rulesForUpdate

默认值

以下参数在Base Api控制器中设置,并且可以在特定情况下被您的控制器重写

  • protected $resourceSingle = JsonResource::class; 用于单个资源的集合
  • protected $resourceCollection = ResourceCollection::class; 用于资源集合的集合
  • protected $defaultFields = ['*']; 默认字段以响应
  • protected $defaultSort = null; 设置查询的默认排序。
  • protected $defaultLimit = 25; 如果未指定,则一次显示的项目数量。 (0 = 最大限制)
  • protected $maximumLimit = 0; 可以通过 $_GET['limit'] 设置的最大限制。 - 这与默认限制相关联,如果想要禁用分页,则两者都应为0。 ) 将允许在一次调用中返回所有记录。
  • protected $unguard = false; 在创建/更新之前需要取消对模型的保护吗?

作用域

软删除记录

Phpsa\LaravelApiController\Model\Scopes\WithSoftDeletes 特性添加到您的模型中,添加到您的资源文件中

class MyModelResource extends ApiResource
{

 protected static $allowedScopes = [
        'withTrashed',
        'onlyTrashed'
    ];

现在可以在查询中附加 withTrashed=1onlyTrashed=1

响应

可以通过覆盖以下受保护的方 法来覆盖每个点的响应

  • handleIndexResponse
  • handleStoreResponse
  • handleShowResponse
  • handleUpdateResponse
  • handleDestroyResponse

性能提示

缓存表列定义

– 在 https://github.com/phpsa/laravel-api-controller/pull/118/files 中引入 – 添加了缓存表定义的能力,以减少获取表列的调用次数,可以在配置文件中启用或设置 PHPSA_API_CACHE_TABLE_COLUMNS 变量为 true。

原始分页获取

– handleIndexAction 将使用完整的 Eloquent 模型,handleIndexActionRaw 将绕过 Eloquent 并使用来自数据库的原始响应。

安全

如果您发现任何与安全相关的问题,请通过电子邮件而不是使用问题跟踪器来报告。

鸣谢

赞助商