swisnl / json-api-client
一个用于将远程 JSON:API 资源映射到 Eloquent 模型和集合的 PHP 扩展包。
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-09-15 08:01:37 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
N.B. 在安装此扩展包之前,请确保您已安装了 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 实例并在您的类中使用它。或者,您可以创建一个 仓库。
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
来定义自己的模型。如果您想定义隐藏属性、类型转换或获取/设置修改器,这可能很有用。如果您使用自定义模型,您必须使用 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
或为复数关系传递空数组来取消设置关系。
注意:形态关系需要数据中存在'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
实例。可以使用DocumentFactory
轻松地创建此类文档,只需提供DataInterface
实例即可。这可以是一个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
分别解析'raw' \Psr\Http\Message\ResponseInterface
或简单的json字符串。
变更日志
有关最近更改的更多信息,请参阅CHANGELOG。
测试
composer test
贡献
请参阅CONTRIBUTING和CODE_OF_CONDUCT以获取详细信息。
安全
如果您发现任何安全相关的问题,请通过电子邮件security@swis.nl联系,而不是使用问题跟踪器。
许可证
MIT许可证(MIT)。请参阅许可证文件获取更多信息。
本软件包是Treeware。如果您在生产环境中使用它,我们希望您购买一棵树以感谢我们的工作。通过为Treeware森林做出贡献,您将为当地家庭创造就业机会并恢复野生动物栖息地。
SWIS ❤️ 开源
SWIS是一家来自荷兰莱顿的网站代理机构。我们热爱与开源软件合作。