rstgroup / json-api-php

一个用于创建json-api标准响应的PHP库(参见:http://jsonapi.org)

1.0.0 2014-08-01 11:17 UTC

This package is not auto-updated.

Last update: 2024-09-24 07:49:21 UTC


README

Made with passion - RST Group Code Climate Build Status Test Coverage

这是一个用于创建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类中,这些将是firstNamelastName,因为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'。