soyhuce/laravel-json-resources

针对 Laravel 的具有观点的 JsonResource

1.6.0 2024-03-08 14:46 UTC

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status GitHub PHPStan Action Status Total Downloads

永远不要在生产环境中在 JsonResource 序列化时执行数据库查询,不要产生开销!

安装

您可以通过 composer 安装此包

composer require soyhuce/laravel-json-resources

您可以使用以下命令发布配置文件

php artisan vendor:publish --tag="json-resources-config"

用法

此包提供了一个基类 Soyhuce\JsonResources\JsonResource

它提供了一个简化的数据序列化接口

class UserResource extends \Soyhuce\JsonResources\JsonResource
{
    /**
     * @return array<string, mixed>
     */
    public function format(): array
    {
        return [
            'id' => $this->id,
            'email' => $this->email,
        ];
    }
}

仍然可以使用 public function toArray($request): array 方法。

还提供了一个基类 \Soyhuce\JsonResources\ResourceCollection

禁止执行数据库查询

使用此基资源,您可以在序列化期间禁止执行数据库查询。

您只需在 json-resources.forbid-database-queries 配置中激活此选项。

此配置旨在用于本地环境,但在生产环境中应关闭。它允许您测试所有必需的关系是否正确加载(例如在功能测试中),同时在生产环境中限制开销。

例如

class UserResource extends \Soyhuce\JsonResources\JsonResource
{
    /**
     * @return array<string, mixed>
     */
    public function format(): array
    {
        return [
            'id' => $this->id,
            'role' => $this->role->label,
        ];
    }
}

class UserController
{
    public function index(): \Illuminate\Http\Resources\Json\JsonResource
    {
        return UserResource::collection(User::all());
    }
}

每次执行请求都会引发一个 DatabaseQueryDetected 异常

2 queries detected in resource :
select * from "roles" where "id" = 2
select * from "roles" where "id" = 1

匿名资源

在某些情况下,可能需要匿名资源。

例如,如果用户的角色是可空的,则可以

class UserResource extends \Soyhuce\JsonResources\JsonResource
{
    /**
     * @return array<string, mixed>
     */
    public function format(): array
    {
        return [
            'id' => $this->id,
            'role' => $this->role !== null 
                ? [
                    'id' => $this->role->id,
                    'label' => $this->role->label,
                ]
                : null
        ];
    }
}

通过一个 AnonymousResource,您可以这样做

class UserResource extends \Soyhuce\JsonResources\JsonResource
{
    /**
     * @return array<string, mixed>
     */
    public function format(): array
    {
        return [
            'id' => $this->id,
            'role' => new \Soyhuce\JsonResources\AnonymousResource(
                $this->role,
                fn (Role $role) => [
                    'id' => $this->role->id,
                    'label' => $this->role->label,
                ]
            )
        ];
    }
}

如果用户有一个角色,您将得到

{
  "data": {
    "id": 1,
    "role": {
      "id": 2,
      "label": "classic"
    }
  }
}

如果用户没有角色,您将得到

{
  "data": {
    "id": 1,
    "role": null
  }
}

从控制器返回匿名资源

从控制器返回匿名资源是可能的。如果提供的数据为 null,您不会得到 'null',而是一个空数组 []

class SomeController
{
    public function show()
    {
        return \Soyhuce\JsonResources\AnonymousResource::make(
           $this->searchSomeItem(), // returns Item|null
           fn (Item $item) => [
               'label' => $item->label,
               'prop' => $item->prop 
           ]
        );
    }
}

如果找到了项目

{
  "data": {
    "label": "the label",
    "prop": 14
  }
}

如果项目是 null

{
  "data": []
}

注意:不支持匿名资源集合

无格式的匿名资源

您可以选择不带回调返回匿名资源。在这种情况下,资源将原样返回。

class SomeController
{
    public function show()
    {
        $foo = $this->fetchFoo();
        $bars = $this->fetchBars();
        
        return \Soyhuce\JsonResources\AnonymousResource::make([
           'foo' => FooResource::make($foo),
           'bars' => BarResource::collection($bars), 
       ]);
    }
}

返回的 json 将具有以下形式

{
  "data": {
    "foo": {
      // some foo data
    },
    "bar": [
      {
        // some bar data
      },
      {
        // some bar data
      }
    ]
  }
}

Json 编码

默认情况下,所有返回的 json 都使用 JSON_PRESERVE_ZERO_FRACTION 选项进行编码。

功能测试

当应用处于 localtesting 环境时,JsonResourceResourceCollection 类会在响应中添加一个 X-Json-Resource 标头。

此标头将包含用于生成响应的实际资源类,可用于功能测试以验证使用了哪个资源。

为了测试这一点,您可以使用 TestResponse::assertJsonResource(TheResource::class) 方法。

use Soyhuce\JsonResources\Tests\Fixtures\User;

class SomeController
{
    public function index(): JsonResouce
    {
        return UserResource::collection(User::all());
    }
    
    public function show(User $user): JsonResource
    {
        return UserResource::make($user);
    }

}

class UserTest extends TestCase
{
    /** @test */
    public function indexUsesUserResource(): void
    {
        $this->getJson('users')
            ->assertOk()
            ->assertJsonResource(UserResource::class);
    }
    
    /** @test */
    public function showUsesUserResource(): void
    {
        $user = User:::factory()->createOne();
        
        $this->getJson("users/{$user->id}")
            ->assertOk()
            ->assertJsonResource(UserResource::class);
    }
}

单元测试

JsonResource 进行单元测试可能很有用。您可以使用 soyhuce/laravel-testing 进行此操作。

在这种情况下,您可能需要允许执行数据库查询。您可以通过调用

\Soyhuce\JsonResources\JsonResources::allowDatabaseQueries();

测试

composer test

变更日志

请参阅 CHANGELOG 了解最近更改的详细信息。

贡献

请参阅 CONTRIBUTING 了解详细信息。

安全漏洞

请查看我们如何报告安全漏洞的安全策略

致谢

许可协议

MIT 许可协议(MIT)。有关更多信息,请参阅许可文件