intermax / laravel-json-api
可重用的过滤器、资源和其他API工具。
Requires
- php: >=8.2
- ext-json: *
- intermax/laravel-open-api: ^2.0
- laravel/framework: ^10.0 || ^11.0
- spatie/laravel-json-api-paginate: ^1.12
- spatie/laravel-query-builder: ^5.0
Requires (Dev)
- larastan/larastan: ^2.2
- laravel/pint: ^1.1
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
README
Laravel API是一个根据JSON:API规范快速构建API的包。它还可以根据您的API端点动态生成OpenAPI文档。
安装
composer require intermax/laravel-json-api
配置
要按JSON:API格式渲染异常,可以将中间件Intermax\LaravelJsonApi\Middleware\RenderJsonApiExceptions
添加到相应的路由中。一个合理的示例是在API中间件组中的HTTP Kernel中
// app/Http/Kernel.php use Intermax\LaravelJsonApi\Middleware\RenderJsonApiExceptions; ... protected $middlewareGroups = [ ... 'api' => [ ... RenderJsonApiExceptions::class, ], ];
基本用法
要创建端点,您只需创建一个Laravel路由,就像您通常所做的那样。之后,您需要创建一个资源。假设我们创建一个返回UserResource
的端点
use Illuminate\Http\Request; use Intermax\LaravelJsonApi\Resources\JsonApiResource; class UserResource extends JsonApiResource { public function getAttributes(Request $request): array { return [ 'email' => $this->resource->email, 'name' => $this->resource->name, ]; } public function getRelations(Request $request): ?array { return null; } public function getLinks(Request $request): ?array { return null; } }
之后,您应该创建控制器方法。通常您会想要一个API资源控制器。如果您返回您刚刚创建的资源,您就使用Laravel API包创建了自己的第一个端点
use Illuminate\Http\Request; use App\Http\Resources\UserResource; class UserController { public function show(User $user): UserResource { return new UserResource($user); } }
❗ 提示:确保在控制器方法中对资源进行类型提示,以便可以正确生成Open API文档。有关更多信息,请参阅Open API生成
查询参数
您可能想要应用过滤器、排序和包含到请求中。在JSON:API中,这是通过将filter
、sort
和include变量应用于查询字符串来完成的
/users?filter[isAdmin]=true&sort=name&include=team
CollectionRequest
使用此包,您可以配置一些预定义的过滤器或添加自己的过滤器。您还可以添加包含和排序。为此,您可以在控制器方法中添加一个CollectionRequest
。这本质上是一个扩展的FormRequest。您的自定义CollectionRequest
需要扩展Intermax\LaravelJsonApi\Requests\CollectionRequest
。它可能看起来像这样
namespace App\Http\Requests; use Intermax\LaravelJsonApi\Filters\ScopeFilter; use Intermax\LaravelJsonApi\Filters\OperatorFilter; use Intermax\LaravelJsonApi\Requests\CollectionRequest; use Intermax\LaravelJsonApi\Sorts\Sort; use Intermax\LaravelJsonApi\Includes\Relation; class UserCollectionRequest extends CollectionRequest { public function filters(): array { return [ new OperatorFilter('createdAt'), new ScopeFilter('isAdmin'), ]; } public function sorts(): array { return [ new Sort('name'), ]; } public function includes(): array { return [ new Relation('team'), // Eloquent relation name ]; } }
此特定的CollectionRequest
添加了两个过滤器,filter[createdAt]
和filter[isAdmin]
。要了解这些特定过滤器的工作方式,请参阅过滤器类型。
控制器
为了使过滤器、包含和排序真正发挥其魔法作用,我们还需要更多。在控制器中,需要使用QueryResolver
来将过滤器应用于Eloquent查询。在底层,这使用了spatie的laravel-query-builder包。
use Intermax\LaravelJsonApi\Requests\QueryResolver; class UserController { public function index(UserCollectionRequest $request, QueryResolver $queryResolver): UserResourceCollection { $query = User::query(); $queryResolver->resolve($request, $query); $query->where(...) // You can alter the query further if needed return new UserResourceCollection($query->jsonPaginate()); } }
过滤器类型
此包提供了两种内置过滤器。一种是ScopeFilter
。正如其名称所暗示的,这将使用发送的值调用作用域。
第二种称为OperatorFilter
。它允许您使用一组运算符进行查询
- 等于:
filter[column]=value
或filter[column][eq]=value
- 不等于:
filter[column][nq]=value
- 大于:
filter[column][gt]=value
- 小于:
filter[column][lt]=value
- 大于等于:
filter[column][gte]=value
- 小于等于:
filter[column][lte]=value
- 包含:
filter[column][contains]=value
可以指定允许的运算符(默认全部允许)
use Intermax\LaravelJsonApi\Filters\OperatorFilter new OperatorFilter( fieldName: 'name', allowedOperators: [ 'eq', 'nq', 'contains', ], );
变异请求
对于POST、PUT或PATCH请求,此包提供了一个可扩展的基本请求以方便使用。您应该使用MutationRequest
而不是Laravel中已知的常规FormRequest
。此类帮助您遵守JSON:API规范进行请求
- 它拒绝没有正确内容类型(
application/vnd.api+json
)的请求 - 实现类可以为
attributes
和relationships
字段提供规则 - 它有用于检索
validatedAttributes
和validatedRelationships
的方法
⚠️ 警告:请注意,如果您之前没有检查内容类型,您可能会很容易地在您的应用程序中创建破坏性更改。
用法
一个示例实现可能看起来像这样
use Intermax\LaravelJsonApi\Requests\MutationRequest; class UserUpdateRequest extends MutationRequest { protected function type(): string { return 'users'; } protected function attributeRules(): array { return [ 'email' => ['email'], 'name' => ['string'], ] } }
在控制器中,您可以使用这些方法来检索已验证的字段
public function update(UserUpdateRequest $request, User $user): UserResource { $user->update($request->validatedAttributes()); return new UserResource($user); }
OpenAPI生成
此包利用Laravel Open API包提供/docs
端点(以及/docs/json
和/docs/yaml
端点)。
Open API包将扫描api路由,读取FormRequests,确定ApiResources并尝试猜测资源的输出。我们的目标是使用最少的配置生成尽可能多的文档。
为了使此功能最佳,需要满足一些条件
- 对于'collection-type'端点,即使不使用Eloquent,也要使用CollectionRequest,它仍然能够推断查询参数。您甚至可以使用规则验证查询参数。
- 在控制器方法中为要返回的资源指定类型提示。
- 对于POST/PUT/PATCH端点,使用FormRequest验证,并包括其中的所有字段(即使它们没有验证)。包使用此信息来确定请求体。
- 包将从资源数组中获取所有字段/属性。
改进资源属性数据类型
如果您查看文档并看到所有资源属性都在数组中以字符串形式列出,您还可以采取一项额外措施来改进它。将每个字段包装在Intermax\LaravelOpenApi\Generator\Values\Value
类型对象中
use Intermax\LaravelOpenApi\Generator\Values\StringValue; use Intermax\LaravelOpenApi\Generator\Values\IntegerValue; use Intermax\LaravelOpenApi\Generator\Values\NumberValue; use Intermax\LaravelOpenApi\Generator\Values\DateTimeValue; use Intermax\LaravelOpenApi\Generator\Values\BooleanValue; use Carbon\Carbon; use Intermax\LaravelJsonApi\Resources\JsonApiResource; class UserResource extends JsonApiResource { public function getAttributes(Request $request): array { return [ 'age' => new IntegerValue(fn () => Carbon::now()->diffInYears($this->resource->birthDate)), 'email' => new StringValue(fn () => $this->resource->email), 'name' => new StringValue(fn () => $this->resource->name), 'createdAt' => new DateTimeValue(fn () => $this->resource->created_at), 'isAdmin' => new BooleanValue(fn () => $this->resource->is_admin), ]; } }