rstgroup / json-api-php
一个用于创建json-api标准响应的PHP库(参见:http://jsonapi.org)
Requires
- php: >=5.3.0
Requires (Dev)
- phpunit/phpunit: 4.1
This package is not auto-updated.
Last update: 2024-09-24 07:49:21 UTC
README
这是一个用于创建json-api标准响应的轻量级PHP库。如果您想了解更多关于json-api标准的信息,请参阅http://jsonapi.org的详细信息。
关于此文档
此文档尚未完成,目前只是一个草稿。
使用方法
快速示例
<?php use RstGroup\JsonApiPhp\EntityInterface; use RstGroup\JsonApiPhp\Resource; use RstGroup\JsonApiPhp\Relation; use RstGroup\JsonApiPhp\Writer; // define posts resource $postsResource = new Resource(); $postsResource->setCollectionName('posts'); $postsResource->setName('post'); $postsResource->setHref('/posts/{posts.id}'); // create post entities $post1 = new Post(1, 'first post'); $post2 = new Post(2, 'second awesome post'); $postsResource->setEntities(array($post1, $post2)); // render result $writer = new Writer(); echo json_encode($writer->write($postsResource));
{"posts": [ { "id": "1", "href": "/posts/1", "text": "first post" }, { "id": "2", "href": "/posts/2", "text": "second awesome post" } ]}
简介
以“作者”和“帖子”两个资源为例。作者创建帖子;他可以有很多帖子,但每个帖子只与一个作者相关。
我们首先应该做的事情是定义我们的资源。
定义资源
每个资源类都应该扩展(惊喜!)Resource
类。但是,如果您不想,您不需要从Resource
类继承。例如
<?php use RstGroup\JsonApiPhp\EntityInterface; use RstGroup\JsonApiPhp\Resource; use RstGroup\JsonApiPhp\Relation; use RstGroup\JsonApiPhp\Writer; // authors $authorsResource = new Resource(); $authorsResource->setCollectionName('authors'); $authorsResource->setName('author'); $authorsResource->setHref('/authors/{authors.id}'); // posts $postsResource = new Resource(); $postsResource->setCollectionName('posts'); $postsResource->setName('post'); $postsResource->setHref('/posts/{posts.id}');
如您所见,每个资源都需要定义这三个属性
- 集合名称
- 名称
- href
其中前两个由Writer
类用于应用标准指定的命名约定。“href”用于准备特定资源的完整链接。Name
代表单个实体的名称。Collection name
代表资源实体的复数名称,而href
应包含单个资源实体的URL模板。
现在我们已经知道了我们有哪些类型的资源,它们的名称、集合名称和链接。您可能已经注意到,链接被设置为具有占位符的模板,例如{authors.id}
。正确准备的占位符由Writer
用于确定哪些部分应替换为值(以及一些其他事情)。这对于更复杂的链接将很有用,例如:/authors/{authors.id}/posts/{posts.id}
。
重要的是要知道,占位符必须具有以下形式:{RESOURCE_COLLECTION_NAME.id}
现在我们来定义实体。
定义实体
我们首先想要创建的实体是Author
。如您所见,Author
类实现了EntityInterface
。这意味着应该实现两个方法:getId()
和toArray()
。
getId()
返回特定实体的id,它被用于库的每个地方,其中需要知道该id。
toArray()
应返回响应表示中被视为资源字段的Author
类的那些属性。在Author
类中,这些将是firstName
和lastName
,因为id
将自动添加。
class Author implements EntityInterface { protected $id; protected $firstName; protected $lastName; public function __construct($id, $firstName, $lastName) { $this->id = $id; $this->firstName = $firstName; $this->lastName = $lastName; } public function getId() { return $this->id; } public function toArray() { return array( 'firstName' => $this->firstName, 'lastName' => $this->lastName, ); } }
class Post implements EntityInterface { protected $id; protected $text; public function __construct($id, $text) { $this->id = $id; $this->text = $text; } public function getId() { return $this->id; } public function toArray() { return array( 'text' => $this->text, ); } }
现在我们可以准备我们的第一个json-api标准结果了
... $author1 = new Author(10, 'John', 'Doe'); $author2 = new Author(20, 'Adam', 'Novak'); $authorsResource->setEntities(array($author1, $author2)); $writer = new Writer(); $result = $writer->write($authorsResource); echo json_encode($result);
结果看起来像这样
{"authors": [ { "id": "10", "href": "/authors/10", "firstName": "John", "lastName": "Doe" }, { "id": "20", "href": "/authors/20", "firstName": "Adam", "lastName": "Novak" } ]}
所以,我们有一个作者列表,可以通过例如http://api.example.com/authors
链接来获取,但关于相关资源如posts
怎么办?嗯,为了这个,我们需要定义资源关系。
添加资源关系
一对一
如前所述,每个帖子都可以与一个作者有一个关系。假设我们有两个帖子,它们是由同一作者添加的。为此,我们需要稍微修改一下Post
类
class Post implements EntityInterface { ... /** * @var Author */ protected $author; /** * @param Author $author * @return $this */ public function setAuthor(Author $author) { $this->author = $author; return $this; } /** * @return Author */ public function getAuthor() { return $this->author; } ... }
我们在这里所做的是添加了$author
属性及其setter和getter方法。作者应该是Author
实体的一个实例。
提示:我们没有将
$author
属性添加到由toArray()
方法返回的实体字段列表中,因为$author
的唯一用途是存储与帖子相关的作者数据。
... $author1 = new Author(10, 'John', 'Doe'); $post1 = new Post(1, 'first post'); $post1->setAuthor($author1); $post2 = new Post(2, 'second awesome post'); $post2->setAuthor($author1); $postsResource->setEntities(array($post1, $post2));
设置器(setter)不如获取器(getter)重要——我们也可以使用构造函数来设置——但在此情况下,获取器方法必须严格命名为 getAuthor()
。为什么?因为 Writer
会根据以下信息自动确定所需获取器方法的名称:
- 关系类型(一对一/一对多)
- 资源名称/集合名称(此处:author/authors)
在我们的例子中,一个帖子与一个作者相关联,因此关系类型是 一对一
。因此,将使用 "author" 资源名称作为获取器方法的名称。在 一对多
关系的情况下(例如,作者拥有多个帖子),将使用 Post
资源的集合名称("posts")。因此,Author
实体类中的 getPosts()
将作为获取器方法。
无论如何,我们创建帖子及其作者之间的关系如下
... $postsResource->addRelation(new Relation(Relation::TO_ONE, $authorsResource)); $writer = new Writer(); $result = $writer->write($postsResource); echo json_encode($result);
结果如下
{"posts": [ { "id": "1", "href": "/posts/1", "text": "first post", "links": { "author": { "id": "10", "href": "/authors/10", "type": "authors" } } }, { "id": "2", "href": "/posts/2", "text": "second awesome post", "links": { "author": { "id": "10", "href": "/authors/10", "type": "authors" } } } ]}
如您所见,在每个帖子实体中添加了新的字段 links
。
字段 links
包含有关特定帖子的作者的一些简要信息。它可以以两种(一对多关系类型为三种)形式返回:要么是 id,要么是对象。默认情况下,后者是这种情况,要更改此行为,您需要调用
$writer->setLinkForm(Writer::AS_ID);
结果如下
{"posts": [ { "id": "1", "href": "/posts/1", "text": "first post", "links": { "author": "10" } }, { "id": "2", "href": "/posts/2", "text": "second awesome post", "links": { "author": "10" } } ] ...
或者您可以——出于某种奇怪的原因——将其关闭
$writer->attachResourceObjectsLinks(false);
结果如下
{"posts": [ { "id": "1", "href": "/posts/1", "text": "first post", }, { "id": "2", "href": "/posts/2", "text": "second awesome post", } ], ...
一对多
到目前为止,我们知道如何定义 一对一
关系(帖子有一个作者),但关于 一对多
关系(作者有帖子)呢?以下是在 Author
类中如何操作:
class Author implements EntityInterface { ... /** * @var Post[] */ protected $posts; /** * @param Post[] $posts * @return $this */ public function setPosts(array $posts) { $this->posts = $posts; return $this; } /** * @return Post[] */ public function getPosts() { return $this->posts; } ... }
...然后
$post1 = new Post(1, 'first post'); $post2 = new Post(2, 'second awesome post'); $author1 = new Author(10, 'John', 'Doe'); $author1->setPosts(array($post1, $post2)); $authorsResource->setEntities(array($author1)); $authorsResource->addRelation(new Relation(Relation::TO_MANY, $postsResource)); $writer = new Writer(); echo json_encode($writer->write($authorsResource));
因此,结果看起来像这样
{"authors": [ { "id": "10", "href": "/authors/10", "firstName": "John", "lastName": "Doe", "links": { "posts": { "ids": ["1", "2"], "href": "/posts/1,2", "type": "posts" } } } ]}
嵌入相关资源数据
如前一小节所述,添加关系会导致在每个实体中出现 links
字段。然而,如果您想在响应表示中嵌入相关资源的数据,则应使用适当的值填充实体对象,并打开将 linked
对象附加到表示,因为它默认是禁用的。
在我们的例子中,填充已经完成,因为我们已经通过构造函数设置了所有属性
$author1 = new Author(10, 'John', 'Doe'); ... $post1 = new Post(1, 'first post'); $post2 = new Post(2, 'second awesome post');
所以只剩下最后一件事要做
$writer->setAttachLinked(true);
如您所见,顶级 linked
字段包含有关作者资源相关资源的实体数据
{"authors": [ { "id": "10", "href": "/authors/10", "firstName": "John", "lastName": "Doe", "links": { "posts": { "ids": ["1", "2"], "href": "/posts/1,2", "type": "posts" } } } ], "linked": { "posts": [ { "id": "1", "href": "/posts/1", "text": "first post" }, { "id": "2", "href": "/posts/2", "text": "second awesome post" } ] }}
URL 模板
可以使用 URL 模板来描述资源的 URL 格式,根据其类型。您可以添加您的 URL 模板如下
$authorTemplate = new Template('posts.author', '/authors/{posts.author.id}', 'authors'); $postsResource->addTemplate($authorTemplate);
{ "links": { "posts.author": "/authors/{posts.author.id}" }, "posts": [ { "id": "1", "href": "/posts/1", "text": "first post", "links": { "author": { "id": "111", "href": "/authors/111", "type": "authors" } } }, ... }
待办事项
- 支持 'meta'。