swisnl / jsonapi
Requires
- php: ^7.4|^8.0
- ext-json: *
- illuminate/collections: ^8.0|^9.0|^10.0|^11.0
- illuminate/contracts: ^8.0|^9.0|^10.0|^11.0
- php-http/discovery: ^1.9
- psr/http-client: ^1.0
- psr/http-client-implementation: ^1.0
- psr/http-factory: ^1.0
- psr/http-factory-implementation: ^1.0
- psr/http-message: ^1.0|^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- guzzlehttp/guzzle: ^7.3
- php-http/mock-client: ^1.2
- phpunit/phpunit: ^9.5
Suggests
- swisnl/php-http-fixture-client: Allows for easily mocking API calls with fixtures in your tests
- 2.4.0
- dev-master / 2.3.x-dev
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.1
- 2.0.0
- 2.0.0-beta
- 1.x-dev
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.0
- 1.1.1
- 1.1.0
- 1.0.2
- 1.0.1
- 1.0.0
- 1.0.0-beta.3
- 1.0.0-beta.2
- 1.0.0-beta
- 0.20.0
- 0.19.0
- 0.18.0
- 0.17.0
- 0.16.0
- 0.15.0
- 0.14.0
- 0.13.0
- 0.12.1
- 0.12.0
- 0.11.0
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.0
- 0.8.0
- 0.7.5
- 0.7.4
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.0
- 0.5.0
- 0.4.0
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.3
- 0.2.2
- 0.2.1
- 0.2.0
- 0.1.1
- 0.1.0
- 0.0.3
- 0.0.2
- 0.0.1
- dev-feature/phpstan
- dev-test-less-deps-no-dicovery
- dev-test-less-deps
This package is auto-updated.
Last update: 2024-05-15 07:18:08 UTC
README
一个将远程 JSON:API 资源映射到 Eloquent 类型和集合的 PHP 包。
💡 在开始之前,请注意,这个库只能用于 JSON:API 资源,并需要一些关于该规范的基本知识。如果您不熟悉 {json:api},请阅读 Björn Brala 的优秀博客,快速了解介绍。
安装
ℹ️ 使用 Laravel?请查看 swisnl/json-api-client-laravel 以轻松集成 Laravel。
composer require swisnl/json-api-client
注意。在安装此包之前,请确保您已安装 PSR-18 HTTP 客户端和 PSR-17 HTTP 工厂,或者同时安装它们,例如 composer require swisnl/json-api-client guzzlehttp/guzzle:^7.3
。
HTTP 客户端
借助 PSR-18 HTTP 客户端 和 PSR-17 HTTP 工厂,我们与任何 HTTP 消息客户端解耦。这需要额外的包提供 psr/http-client-implementation 和 psr/http-factory-implementation。例如,要使用 Guzzle 7,只需添加 guzzlehttp/guzzle
。
composer require guzzlehttp/guzzle:^7.3
如果您想使用自己的 HTTP 客户端或使用特定的配置选项,请参阅 HTTP 客户端。
入门
您可以直接创建 DocumentClient 的实例并在您的类中使用它。或者,您可以创建一个 repository。
use Swis\JsonApi\Client\DocumentClient; $client = DocumentClient::create(); $document = $client->get('https://cms.contentacms.io/api/recipes'); /** @var \Swis\JsonApi\Client\Collection&\Swis\JsonApi\Client\Item[] $collection */ $collection = $document->getData(); foreach ($collection as $item) { // Do stuff with the items }
项目
默认情况下,所有项目都是 \Swis\JsonApi\Client\Item
的实例。该 Item
提供了一个类似 Laravel Eloquent 的基础类。
您可以通过扩展 \Swis\JsonApi\Client\Item
或自己实现 \Swis\JsonApi\Client\Interfaces\ItemInterface
来定义自己的模型。如果您想定义隐藏属性、转换或 get/set 修改器等,这可能很有用。如果您使用自定义模型,您必须使用 TypeMapper 注册它们。
关系
此包实现了类似 Laravel Eloquent 的关系。这些关系提供了一个流畅的接口来检索相关项。目前有四种关系可用
HasOneRelation
HasManyRelation
MorphToRelation
MorphToManyRelation
请参阅以下示例以了解如何定义关系
use Swis\JsonApi\Client\Item; class AuthorItem extends Item { protected $type = 'author'; public function blogs() { return $this->hasMany(BlogItem::class); } } class BlogItem extends Item { protected $type = 'blog'; public function author() { return $this->hasOne(AuthorItem::class); } }
命名支持
应使用 camelCase 方法定义关系。然后可以通过 camelCase 或 snake_case 的魔法属性访问相关项,或使用定义关系时使用的显式名称。
集合
此包使用 Laravel Collections 作为项目数组的包装器。
链接
所有可以具有链接的对象(即文档、错误、项目和相关关系)使用Concerns/HasLinks
,因此具有一个返回Links
实例的getLinks
方法。这是一个简单的类似数组的对象,其中的键值对是Link
实例或null
。
示例
给定以下JSON
{ "links": { "self": "http://example.com/articles" }, "data": [{ "type": "articles", "id": "1", "attributes": { "title": "JSON:API paints my bikeshed!" }, "relationships": { "author": { "data": { "type": "people", "id": "9" }, "links": { "self": "http://example.com/articles/1/author" } } }, "links": { "self": "http://example.com/articles/1" } }] }
您可以通过这种方式获取链接
/** @var $document \Swis\JsonApi\Client\Document */ // Document links $links = $document->getLinks(); echo $links->self->getHref(); // http://example.com/articles // Item links $links = $document->getData()->getLinks(); echo $links->self->getHref(); // http://example.com/articles/1 // Relationship links $links = $document->getData()->author()->getLinks(); echo $links->self->getHref(); // http://example.com/articles/1/author
元数据
所有可以具有元信息对象(即文档、错误、项目、jsonapi、链接和相关关系)使用Concerns/HasMeta
,因此具有一个返回Meta
实例的getMeta
方法。这是一个简单的类似数组的对象,包含键值对。
示例
给定以下JSON
{ "links": { "self": { "href": "http://example.com/articles/1", "meta": { "foo": "bar" } } }, "data": { "type": "articles", "id": "1", "attributes": { "title": "JSON:API paints my bikeshed!" }, "relationships": { "author": { "data": { "type": "people", "id": "9" }, "meta": { "written_at": "2019-07-16T13:47:26" } } }, "meta": { "copyright": "Copyright 2015 Example Corp." } }, "meta": { "request_id": "a77ab2b4-7132-4782-8b5e-d94ebaff6e13" } }
您可以通过这种方式获取元数据
/** @var $document \Swis\JsonApi\Client\Document */ // Document meta $meta = $document->getMeta(); echo $meta->request_id; // a77ab2b4-7132-4782-8b5e-d94ebaff6e13 // Link meta $meta = $document->getLinks()->self->getMeta(); echo $meta->foo; // bar // Item meta $meta = $document->getData()->getMeta(); echo $meta->copyright; // Copyright 2015 Example Corp. // Relationship meta $meta = $document->getData()->author()->getMeta(); echo $meta->written_at; // 2019-07-16T13:47:26
类型映射器
所有自定义模型都必须在TypeMapper
中进行注册。正如其名,TypeMapper
将JSON:API类型映射到自定义项目。
仓库
为了方便,此包包括一个基本的仓库,其中包含几个用于处理资源的方法。您可以根据\Swis\JsonApi\Client\Repository
创建您使用的每个端点的仓库。然后,此仓库将使用所有操作的标准化CRUD端点。
class BlogRepository extends \Swis\JsonApi\Client\Repository { protected $endpoint = 'blogs'; }
上述仓库将包含所有CRUD操作的方方法。如果您正在与只读API一起工作,并且不想执行所有操作,您可以扩展\Swis\JsonApi\Client\BaseRepository
并仅包括所需的操作/特性。
use Swis\JsonApi\Client\Actions\FetchMany; use Swis\JsonApi\Client\Actions\FetchOne; class BlogRepository extends \Swis\JsonApi\Client\BaseRepository { use FetchMany; use FetchOne; protected $endpoint = 'blogs'; }
如果此仓库(模式)不符合您的需求,您可以使用此包提供的客户端创建自己的实现。
请求参数
仓库提供的所有方法都接受额外的参数,这些参数将被附加到URL上。这可以用于添加包含和/或分页参数。
$repository = new BlogRepository(); $repository->all(['include' => 'author', 'page' => ['limit' => 15, 'offset' => 0]]);
项目填充器
ItemHydrator
可用于使用具有属性的关联数组填充/填充项目及其关系。如果您想使用请求的POST数据填充项目,这很有用。
$typeMapper = new TypeMapper(); $itemHydrator = new ItemHydrator($typeMapper); $blogRepository = new BlogRepository(DocumentClient::create($typeMapper), new DocumentFactory()); $item = $itemHydrator->hydrate( $typeMapper->getMapping('blog'), request()->all(['title', 'author', 'date', 'content', 'tags']), request()->id ); $blogRepository->save($item);
关系
ItemHydrator
还会填充(嵌套)关系。必须显式在项目中的$availableRelations
数组中列出关系,以便进行填充。如果我们取上述示例,我们可以使用以下属性数组来填充一个新的博客项目
$attributes = [ 'title' => 'Introduction to JSON:API', 'author' => [ 'id' => 'f1a775ef-9407-40ba-93ff-7bd737888dc6', 'name' => 'Björn Brala', 'homepage' => 'https://github.com/bbrala', ], 'co-author' => null, 'date' => '2018-12-02 15:26:32', 'content' => 'JSON:API was originally drafted in May 2013 by Yehuda Katz...', 'media' => [], 'tags' => [ 1, 15, 56, ], ]; $itemDocument = $itemHydrator->hydrate($typeMapper->getMapping('blog'), $attributes); echo json_encode($itemDocument, JSON_PRETTY_PRINT); { "data": { "type": "blog", "attributes": { "title": "Introduction to JSON:API", "date": "2018-12-02 15:26:32", "content": "JSON:API was originally drafted in May 2013 by Yehuda Katz..." }, "relationships": { "author": { "data": { "type": "author", "id": "f1a775ef-9407-40ba-93ff-7bd737888dc6" } }, "co-author": { "data": null }, "media": { "data": [] }, "tags": { "data": [{ "type": "tag", "id": "1" }, { "type": "tag", "id": "15" }, { "type": "tag", "id": "56" }] } } }, "included": [{ "type": "author", "id": "f1a775ef-9407-40ba-93ff-7bd737888dc6", "attributes": { "name": "Björn Brala", "homepage": "https://github.com/bbrala" } }] }
如示例所示,关系可以通过id或通过包含id和更多属性的关联数组进行填充。如果项目使用关联数组进行填充,除非在关系上调用setOmitIncluded(true)
,否则它将包含在结果JSON中。您可以通过为单数关系传递null
或为复数关系传递空数组来取消关系。
注意。Morph关系需要数据中存在'type'属性,以便知道应创建哪种类型的项目。
处理错误
请求可能会因多个原因而失败,处理方式取决于发生了什么。如果DocumentClient
遇到错误,基本有三种选择。
非2xx请求且没有主体
如果响应没有成功的状态码(2xx)并且没有主体,则DocumentClient
(因此也是Repository
)将返回一个InvalidResponseDocument
实例。
非2xx请求且有无效的JSON:API主体
如果响应没有成功的状态码(2xx)并且有主体,它将被解析为JSON:API文档。如果响应不能解析为该文档,将抛出ValidationException
。
非2xx请求具有有效的JSON:API正文
如果响应没有成功的状态码(2xx)但有正文,则将其解析为JSON:API文档。在这种情况下,DocumentClient
(因此也是Repository
)将返回一个Document
实例。该文档包含响应中的错误,假设服务器响应了错误。
检查错误
根据上述规则,您可以这样检查错误
$document = $repository->all(); if ($document instanceof InvalidResponseDocument || $document->hasErrors()) { // do something with errors }
客户端
此包提供两个客户端:DocumentClient
和Client
。
DocumentClient
这是您通常会使用的客户端,例如,存储库在内部使用此客户端。根据JSON:API规范,所有请求和响应都是文档。因此,此客户端始终在发布数据时期望一个\Swis\JsonApi\Client\Interfaces\DocumentInterface
作为输入,并且始终返回此相同接口。这可能是一个普通的Document
,当没有数据时,一个ItemDocument
用于一个项目,一个CollectionDocument
用于一个集合,或者当服务器响应非2xx响应时,一个InvalidResponseDocument
。
DocumentClient
在内部遵循以下步骤
- 使用您的HTTP客户端发送请求;
- 使用
ResponseParser
解析和验证响应; - 创建正确的文档实例;
- 通过使用与
TypeMapper
注册的项目模型或一个\Swis\JsonApi\Client\Item
作为后备来为每个项目添加水化; - 为所有关系添加水化;
- 将元数据添加到文档中,例如错误、链接和元。
Client
这是一个更底层的客户端,例如,可以用于发布二进制数据,如图像。它可以接受您的请求工厂接受的任何输入数据,并返回原始的\Psr\Http\Message\ResponseInterface
。它不会解析、验证响应或为项目添加水化!
DocumentFactory
当创建或更新资源时,DocumentClient
需要ItemDocumentInterface
实例。可以通过使用DataInterface
实例来轻松创建此类文档,并将其提供给DocumentFactory
。这可以是一个ItemInterface
,通常由ItemHydrator创建,或者一个Collection
。
HTTP客户端
默认情况下,Client
使用php-http/discovery查找可用的HTTP客户端、请求工厂和流工厂,因此您无需自己设置。您也可以指定自己的HTTP客户端、请求工厂或流工厂。这是向HTTP客户端添加额外选项或为测试注册模拟HTTP客户端的绝佳方式
if (app()->environment('testing')) { $httpClient = new \Swis\Http\Fixture\Client( new \Swis\Http\Fixture\ResponseBuilder('/path/to/fixtures') ); } else { $httpClient = new \GuzzleHttp\Client( [ 'http_errors' => false, 'timeout' => 2, ] ); } $typeMapper = new TypeMapper(); $client = DocumentClient::create($typeMapper, $httpClient); $document = $client->get('https://cms.contentacms.io/api/recipes');
注意。此示例在测试环境中使用我们的swisnl/php-http-fixture-client。此包允许您轻松使用静态固定值模拟请求。绝对值得一试!
高级使用
如果您不喜欢使用提供的存储库或客户端,您也可以使用Parsers\ResponseParser
或Parser\DocumentParser
分别使用\Psr\Http\Message\ResponseInterface
或简单的json字符串进行解析。
变更日志
有关最近更改的更多信息,请参阅CHANGELOG。
测试
composer test
贡献
请参阅CONTRIBUTING和CODE_OF_CONDUCT以获取详细信息。
安全
如果您发现任何与安全相关的问题,请通过电子邮件security@swis.nl而不是使用问题跟踪器。
许可证
本软件采用MIT许可协议。请参阅许可文件以获取更多信息。
本软件包为Treeware。如果您在生产中使用它,我们希望您为世界买一棵树以感谢我们的工作。通过为Treeware森林做出贡献,您将为当地家庭创造就业机会并恢复野生动物栖息地。
SWIS ❤️ 开源
SWIS是来自荷兰莱顿的网页代理机构。我们热爱与开源软件合作。