Project Icon

json-api

Laravel资源包简化JSON,API规范实现

json-api是一个轻量级Laravel API资源包,简化了JSON:API标准的实现。它支持稀疏字段集、复合文档等特性,并提供JsonApiResource类扩展Laravel Eloquent API资源。开发者可以方便地定义资源属性和关系,实现条件逻辑和性能优化,轻松创建符合JSON:API规范的API。

JSON:API Resource: Tim MacDonald 开发的 Laravel 包

Laravel 的 JSON:API Resource

这是一个轻量级的 Laravel API 资源包,可帮助你遵循 JSON:API 标准,内置支持稀疏字段集、复合文档等功能。

注意 这些文档并非旨在介绍 JSON:API 规范及相关概念,如果你还不熟悉,应该前往阅读规范。以下文档仅涵盖如何通过该包来实现规范。

目录

版本支持

  • PHP: 8.1, 8.2, 8.3
  • Laravel: 9.0, 10.0, 11.0

安装

你可以使用 composerPackagist 安装。

composer require timacdonald/json-api

入门

该包提供的 JsonApiResource 类是 Laravel Eloquent API 资源的特殊化。所有公开的 API 仍然可以访问;例如,在控制器中,你可以像使用 Laravel 的 JsonResource 类一样使用 JsonApiResource

<?php

namespace App\Http\Controllers;

use App\Http\Resources\UserResource;
use App\Models\User;

class UserController
{
    public function index()
    {
        $users = User::with([/* ... */])->paginate();

        return UserResource::collection($users);
    }

    public function show(User $user)
    {
        $user->load([/* ... */]);

        return UserResource::make($user);
    }
}

随着我们深入示例,你会看到在内部与该类交互时引入了新的 API,例如,不再使用 toArray() 方法。

创建你的第一个 JSON:API 资源

让我们为 User 模型创建一个 UserResource。在用户资源中,我们将在响应中公开用户的 namewebsitetwitter_handle

首先我们创建一个继承 TiMacDonald\JsonApi\JsonApiResource 的新 API 资源。

<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    //
}

添加属性

现在我们将创建一个 $attributes 属性,并列出我们想要公开的模型属性。

<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    public $attributes = [
        'name',
        'website',
        'twitter_handle',
    ];
}

当向返回 UserResource 的端点发出请求时,例如:

Route::get('users/{user}', fn (User $user) => UserResource::make($user));

将返回以下 JSON:API 格式的数据:

{
  "data": {
    "type": "users",
    "id": "74812",
    "attributes": {
      "name": "Tim",
      "website": "https://timacdonald.me",
      "twitter_handle": "@timacdonald87"
    }
  }
}

🎉 你刚刚创建了你的第一个 JSON:API 资源 🎉

恭喜...真是太刺激了!

现在我们将深入添加资源的关系,但如果你想探索更复杂的属性功能,可以跳到前面:

添加关系

可用的关系可以在 $relationships 属性中指定,类似于 $attributes 属性,但你可以使用键/值对来提供给定关系应使用的资源类。

我们将在资源上提供两个关系:

  • $user->team: 一个 "toOne" / HasOne 关系。
  • $user->posts: 一个 "toMany" / HasMany 关系。
<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    public $attributes = [
        'name',
        'website',
        'twitter_handle',
    ];

    public $relationships = [
        'team' => TeamResource::class,
        'posts' => PostResource::class,
    ];
}

假设键/值对遵循 '{myKey}' => {MyKey}Resource::class 的约定,可以省略类以进一步简化。

<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    public $attributes = [
        'name',
        'website',
        'twitter_handle',
    ];

    public $relationships = [
        'team',
        'posts',
    ];
}
示例请求和响应

客户端现在可以通过 include 查询参数请求这些关系。

GET /users/74812?include=posts,team

注意 除非调用客户端通过 include 查询参数请求,否则不会在响应中暴露关系。这是有意的,是 JSON:API 规范的一部分。

{
  "data": {
    "id": "74812",
    "type": "users",
    "attributes": {
      "name": "Tim",
      "website": "https://timacdonald.me",
      "twitter_handle": "@timacdonald87"
    },
    "relationships": {
      "posts": {
        "data": [
          {
            "type": "posts",
            "id": "25240"
          },
          {
            "type": "posts",
            "id": "39974"
          }
        ]
      },
      "team": {
        "data": {
          "type": "teams",
          "id": "18986"
        }
      }
    }
  },
  "included": [
    {
      "id": "25240",
      "type": "posts",
      "attributes": {
        "title": "So what is `JSON:API` all about anyway?",
        "content": "...",
        "excerpt": "..."
      }
    },
    {
      "id": "39974",
      "type": "posts",
      "attributes": {
        "title": "Building an API with Laravel, using the `JSON:API` specification.",
        "content": "...",
        "excerpt": "..."
      }
    },
    {
      "id": "18986",
      "type": "teams",
      "attributes": {
        "name": "Laravel"
      }
    }
  ]
}

要了解更复杂的关系特性,你可能想跳到前面:

关于预加载的说明

该包不会预加载 Eloquent 关系。如果关系未预加载,该包将即时延迟加载关系。我强烈建议使用 Spatie 的查询构建器包,它将根据 JSON:API 查询参数标准预加载你的模型。

Spatie 提供了关于如何使用该包的全面文档,但我会简要举例说明如何在控制器中使用它。

<?php

namespace App\Http\Controllers;

use App\Http\Resources\UserResource;
use App\Models\User;
use Spatie\QueryBuilder\QueryBuilder;

class UserController
{
    public function index()
    {
        $users = QueryBuilder::for(User::class)
            ->allowedIncludes(['team', 'posts'])
            ->paginate();

        return UserResource::collection($users);
    }

    public function show($id)
    {
        $user = QueryBuilder::for(User::class)
            ->allowedIncludes(['team', 'posts'])
            ->findOrFail($id);

        return UserResource::make($user);
    }
}

深入了解

我们现在已经介绍了在资源上公开属性和关系的基础知识。我们现在将介绍更高级的主题,以便让你获得更大的控制权。

属性

toAttributes()

正如我们在添加属性部分看到的,$attributes 属性是公开资源属性的最快方法。在某些情况下,你可能需要对公开的属性进行更大的控制。如果是这种情况,你可以实现 toAttributes() 方法。这将让你可以访问当前请求并允许条件逻辑。

<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    /**
     * @param  \Illuminate\Http\Request  $request
     * @return array<string, mixed>
     */
    public function toAttributes($request)
    {
        return [
            'name' => $this->name,
            'website' => $this->website,
            'twitter_handle' => $this->twitter_handle,
            'email' => $this->when($this->email_is_public, $this->email, '<private>'),
            'address' => [
                'city' => $this->address('city'),
                'country' => $this->address('country'),
            ],
        ];
    }
}
示例响应
{
  "data": {
    "id": "74812",
    "type": "users",
    "attributes": {
      "name": "Tim",
      "website": "https://timacdonald.me",
      "twitter_handle": "@timacdonald87",
      "email": "<private>",
      "address": {
        "city": "Melbourne",
        "country": "Australia"
      }
    }
  }
}

稀疏字段集

稀疏字段集是 JSON:API 规范的一个特性,允许客户端指定他们想要接收的任何给定资源类型的属性。这允许更确定性的响应,同时也提高了服务器端性能并减小了有效载荷大小。稀疏字段集可以开箱即用于你的资源。

我们将在这里简要介绍它们,但我们建议阅读规范以了解更多信息。

例如,假设我们正在构建博客的索引页面。该页面将显示每篇文章的 titleexcerpt,以及文章作者的 name。如果客户端愿意,他们可以将响应限制为仅包含每种资源类型所需的属性,并排除其他属性,例如文章的 content 和作者的 twitter_handle

为了实现这一点,我们将发送以下请求。

GET /posts?include=author&fields[posts]=title,excerpt&fields[users]=name

注意 include 查询参数键是 author,而稀疏字段集参数键是 users。这是因为作者就是用户,例如 Eloquent author() 关系返回 User 模型。

示例响应
{
  "data": [
    {
      "id": "25240",
      "type": "posts",
      "attributes": {
        "title": "So what is `JSON:API` all about anyway?",
        "excerpt": "..."
      },
      "relationships": {
        "author": {
          "data": {
            "type": "users",
            "id": "74812"
          }
        }
      }
    },
    {
      "id": "39974",
      "type": "posts",
      "attributes": {
        "title": "Building an API with Laravel, using the `JSON:API` specification.",
        "excerpt": "..."
      },
      "relationships": {
        "author": {
          "data": {
            "type": "users",
            "id": "74812"
          }
        }
      }
    }
  ],
  "included": [
    {
      "type": "users",
      "id": "74812",
      "attributes": {
        "name": "Tim"
      }
    }
  ]
}

最小属性

当不使用稀疏字段集时,资源会返回最大的属性有效载荷,即返回资源上声明的所有属性。如果你愿意,你可以要求使用稀疏字段集才能检索任何属性。

你可以在应用程序服务提供者中调用 useMinimalAttributes() 方法。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use TiMacDonald\JsonApi\JsonApiResource;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        JsonApiResource::useMinimalAttributes();

        
对于计算成本较高的属性,可以设置为仅在需要包含在响应中时才进行评估,即它们未通过稀疏字段集或最小属性被排除。如果你在资源中与数据库交互或发送HTTP请求,这可能会很有用。

举个例子,假设我们为每个用户提供一个base64编码的头像。我们的实现是从内部的头像微服务下载头像。

```php
<?php

namespace App\Http\Resources;

use Illuminate\Support\Facades\Http;
use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    /**
     * @param  \Illuminate\Http\Request  $request
     * @return array<string, mixed>
     */
    public function toAttributes($request)
    {
        return [
            // ...
            'avatar' => Http::get("https://avatar.example.com/{$this->id}")->body(),
        ];
    }
}

上面的实现即使在客户端通过稀疏字段集或最小属性排除 avatar 属性时,也会向我们的微服务发送HTTP请求。为了在不返回此属性时提高性能,我们可以将值包装在一个 Closure 中。只有在需要返回 avatar 时,才会评估这个 Closure

<?php

namespace App\Http\Resources;

use Illuminate\Support\Facades\Http;
use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    /**
     * @param  \Illuminate\Http\Request  $request
     * @return array<string, mixed>
     */
    public function toAttributes($request)
    {
        return [
            // ...
            'avatar' => fn () => Http::get("https://avatar.example.com/{$this->id}")->body(),
        ];
    }
}

关系

toRelationships()

正如我们在添加关系部分看到的,$relationships 属性是为资源指定可用关系的最快方式。在某些情况下,你可能需要对提供的关系进行更多控制。如果是这种情况,你可以实现 toRelationships() 方法。这将允许你访问当前请求并进行条件逻辑。

值必须始终包装在一个 Closure 中,只有在客户端请求关系时才会调用。

<?php

namespace App\Http\Resources;

use TiMacDonald\JsonApi\JsonApiResource;

class UserResource extends JsonApiResource
{
    /**
     * @param  \Illuminate\Http\Request  $request
     * @return array<string, (callable(): \TiMacDonald\JsonApi\JsonApiResource|\TiMacDonald\JsonApi\JsonApiResourceCollection|\Illuminate\Http\Resources\PotentiallyMissing)>
     */
    public function toRelationships($request)
    {
        return [
            'team' => fn () => TeamResource::make($this->team),
            'posts' => fn () => $request->user()->is($this->resource)
                ? PostResource::collection($this->posts)
                : PostResource::collection($this->posts->where('published', true)),
        ];
    }
}

自定义关系资源类猜测

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号