willdurand / hateoas
一个支持实现 HATEOAS REST 服务的表示的 PHP 库
Requires
- php: ^7.2 | ^8.0
- doctrine/annotations: ^1.13.2 || ^2.0
- jms/metadata: ^2.0
- jms/serializer: ^3.18.2
- symfony/expression-language: ~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0
Requires (Dev)
- doctrine/coding-standard: ^5.0 | ^8.0
- doctrine/persistence: ^1.3.4 | ^2.0 | ^3.0
- pagerfanta/core: ^2.4 || ^3.0
- phpdocumentor/type-resolver: ^1.5.1
- phpspec/prophecy: ^1.16
- phpspec/prophecy-phpunit: ^2.0.1
- phpunit/phpunit: ^7 | ^9.5.10
- symfony/routing: ~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0
- symfony/yaml: ~3.0 || ~4.0 || ~5.0 || ~6.0 || ~7.0
- twig/twig: ^1.43 || ^2.13 || ^3.0
Suggests
- symfony/routing: To use the SymfonyRouteFactory.
- symfony/yaml: To use yaml based configuration.
- twig/twig: To use the Twig extensions.
- 3.10.0
- 3.9.0
- 3.8.0
- 3.7.0
- 3.6.0
- 3.5.0
- 3.4.0
- 3.3.0
- 3.2.0
- 3.1.0
- dev-master / 3.0.x-dev
- 3.0.0
- 3.0.0-RC1
- 2.12.0
- 2.11.0
- 2.10.0
- 2.9.1
- v2.9.0
- v2.8.1
- v2.8.0
- v2.7.0
- v2.6.0
- v2.5.0
- v2.4.0
- v2.3.0
- v2.2.0
- v2.1.0
- 2.0.x-dev
- v2.0.2
- v2.0.1
- v2.0.0
- v2.0.0-alpha4
- v2.0.0-alpha3
- v2.0.0-alpha2
- v2.0.0-alpha1
- 1.0.x-dev
- 0.0.13
- 0.0.12
- 0.0.11
- 0.0.10
- 0.0.9
- 0.0.8
- 0.0.7
- 0.0.6
- 0.0.5
- 0.0.4
- 0.0.3
- 0.0.2
- 0.0.1
- dev-json-api
This package is auto-updated.
Last update: 2024-09-12 15:18:47 UTC
README
一个支持实现 HATEOAS REST 服务的表示的 PHP 库。
安装
推荐通过 Composer 安装 Hateoas。运行以下命令以要求 willdurand/hateoas
包
composer require willdurand/hateoas
这将解析最新稳定版本。
否则,您可以自己安装库并设置自动加载器。
与 Symfony 一起使用
有一个相应的包!安装 BazingaHateoasBundle 并享受吧!
用法
重要
对于使用
1.0
版本的用户,您可以 跳转到此文档页面。对于使用
2.0
版本的用户,您可以 跳转到此文档页面。以下文档是为 Hateoas 3.0 及以上版本编写的。
介绍
Hateoas 利用 Serializer 库提供了一种构建 HATEOAS REST 服务的优雅方式。HATEOAS 代表 Hypermedia as the Engine of Application State(超媒体作为应用状态引擎),并为您的 表示(即您的 API 响应)添加了 超媒体链接。 HATEOAS 是关于资源上操作的可发现性。
例如,假设您有一个返回单个 用户 表示的用户 API,如下所示
{ "user": { "id": 123, "first_name": "John", "last_name": "Doe" } }
为了告诉您的 API 消费者如何检索此特定用户的数据,您必须向此表示添加您的第一个 链接,让我们将其称为 self
,因为它是此特定用户的 URI
{ "user": { "id": 123, "first_name": "John", "last_name": "Doe", "_links": { "self": { "href": "http://example.com/api/users/123" } } } }
现在让我们深入了解 Hateoas。
配置链接
在 Hateoas 术语中,链接 被视为添加到资源中的 关系。值得一提的是,关系 也指代 嵌入的资源,但这一主题将在 嵌入资源 部分中介绍。
链接是一种通过name
(例如self
)标识的关系,并具有href
参数。
use JMS\Serializer\Annotation as Serializer; use Hateoas\Configuration\Annotation as Hateoas; /** * @Serializer\XmlRoot("user") * * @Hateoas\Relation("self", href = "expr('/api/users/' ~ object.getId())") */ class User { /** @Serializer\XmlAttribute */ private $id; private $firstName; private $lastName; public function getId() {} }
在上面的示例中,我们配置了一个self
关系,它是一个链接,因为它具有href
参数。这个值可能乍一看很奇怪,将在表达式语言部分进行详细说明。这个特殊值用于生成URI。
在本节中,使用注解来配置Hateoas。也支持XML和YAML格式。如果您愿意,也可以使用纯PHP。
重要:您必须以相同的方式配置序列化和Hateoas。例如,如果您使用YAML配置序列化器,则使用YAML配置Hateoas。
尝试HATEOAS的最简单方法是使用HateoasBuilder
。构建器有众多方法来配置Hateoas序列化器,但我们现在不会深入探讨它们(请参阅HateoasBuilder)。一切都会默认正常工作。
use Hateoas\HateoasBuilder; $hateoas = HateoasBuilder::create()->build(); $user = new User(42, 'Adrien', 'Brault'); $json = $hateoas->serialize($user, 'json'); $xml = $hateoas->serialize($user, 'xml');
$hateoas
对象是来自序列化库的JMS\Serializer\SerializerInterface
实例。Hateoas没有自己的序列化器,它钩入JMS序列化器。
默认情况下,Hateoas使用超文本应用语言(HAL)进行JSON序列化。它指定了响应的结构(例如,“links”应位于_links
键下)。
{ "id": 42, "first_name": "Adrien", "last_name": "Brault", "_links": { "self": { "href": "/api/users/42" } } }
对于XML,默认使用Atom Links。
<user id="42"> <first_name><![CDATA[Adrien]]></first_name> <last_name><![CDATA[Brault]]></last_name> <link rel="self" href="/api/users/42"/> </user>
值得一提的是,这些格式是默认的,而不是唯一可用的格式。您可以通过不同的序列化器使用不同的格式,甚至添加您自己的格式。
现在您已经知道了如何添加链接,让我们看看如何添加嵌入资源。
嵌入资源
有时,将相关资源嵌入而不是链接到它们更有效,因为它可以防止客户端发出额外的请求来获取这些资源。
嵌入资源是一个包含数据的命名关系,由embedded
参数表示。
use JMS\Serializer\Annotation as Serializer; use Hateoas\Configuration\Annotation as Hateoas; /** * ... * * @Hateoas\Relation( * "manager", * href = "expr('/api/users/' ~ object.getManager().getId())", * embedded = "expr(object.getManager())", * exclusion = @Hateoas\Exclusion(excludeIf = "expr(object.getManager() === null)") * ) */ class User { ... /** @Serializer\Exclude */ private $manager; }
注意:您需要从序列化中排除manager属性,否则序列化器和Hateoas都会对其进行序列化。当manager为null
时,您还必须排除manager关系,因为否则在创建href
链接(在null
上调用getId()
)时将发生错误。
技巧:如果manager属性是一个已经具有_self
链接的对象,您可以使用该值代替href
,而不是在此处重复它。请参阅LinkHelper。
$hateoas = HateoasBuilder::create()->build(); $user = new User(42, 'Adrien', 'Brault', new User(23, 'Will', 'Durand')); $json = $hateoas->serialize($user, 'json'); $xml = $hateoas->serialize($user, 'xml');
对于json
,HAL表示将嵌入关系放置在_embedded
键中。
{ "id": 42, "first_name": "Adrien", "last_name": "Brault", "_links": { "self": { "href": "/api/users/42" }, "manager": { "href": "/api/users/23" } }, "_embedded": { "manager": { "id": 23, "first_name": "Will", "last_name": "Durand", "_links": { "self": { "href": "/api/users/23" } } } } }
在XML中,序列化embedded
关系将创建新元素。
<user id="42"> <first_name><![CDATA[Adrien]]></first_name> <last_name><![CDATA[Brault]]></last_name> <link rel="self" href="/api/users/42"/> <link rel="manager" href="/api/users/23"/> <manager rel="manager" id="23"> <first_name><![CDATA[Will]]></first_name> <last_name><![CDATA[Durand]]></last_name> <link rel="self" href="/api/users/23"/> </manager> </user>
嵌入资源的标签名称是从序列化配置中的@XmlRoot
注解(在YAML中为xml_root_name
,在XML中为xml-root-name
)推断出来的。
处理集合
库在Hateoas\Representation\*
命名空间中提供了几个类来帮助您处理常见任务。这些是使用库的注解配置的简单类。
《分页表示》类、OffsetRepresentation
类和CollectionRepresentation
类可能是最有趣的一些。当您的资源实际上是一组资源时(例如,/users
是一组用户),这些类非常有用。它们可以帮助您表示集合并添加分页和限制。
use Hateoas\Representation\PaginatedRepresentation; use Hateoas\Representation\CollectionRepresentation; $paginatedCollection = new PaginatedRepresentation( new CollectionRepresentation(array($user1, $user2, ...)), 'user_list', // route array(), // route parameters 1, // page number 20, // limit 4, // total pages 'page', // page route parameter name, optional, defaults to 'page' 'limit', // limit route parameter name, optional, defaults to 'limit' false, // generate relative URIs, optional, defaults to `false` 75 // total collection size, optional, defaults to `null` ); $json = $hateoas->serialize($paginatedCollection, 'json'); $xml = $hateoas->serialize($paginatedCollection, 'xml');
CollectionRepresentation
提供了一个嵌入集合的基本表示。
PaginatedRepresentation
设计用于添加self
、first
,以及在可能的情况下添加last
、next
和previous
链接。
OffsetRepresentation
的工作方式与PaginatedRepresentation
类似,但在分页通过offset
、limit
和total
表示时很有用。
RouteAwareRepresentation
基于给定的路由添加一个self
关系。
您可以通过将PaginatedRepresentation
和RouteAwareRepresentation
中的absolute
参数设置为true
来生成绝对URI
。
Hateoas库还提供了一个PagerfantaFactory
,可以轻松地从Pagerfanta实例构建PaginatedRepresentation
。如果您使用Pagerfanta库,这是一种创建集合表示的更简单方法
use Hateoas\Configuration\Route; use Hateoas\Representation\Factory\PagerfantaFactory; $pagerfantaFactory = new PagerfantaFactory(); // you can pass the page, // and limit parameters name $paginatedCollection = $pagerfantaFactory->createRepresentation( $pager, new Route('user_list', array()) ); $json = $hateoas->serialize($paginatedCollection, 'json'); $xml = $hateoas->serialize($paginatedCollection, 'xml');
您将得到以下JSON内容
{ "page": 1, "limit": 10, "pages": 1, "_links": { "self": { "href": "/api/users?page=1&limit=10" }, "first": { "href": "/api/users?page=1&limit=10" }, "last": { "href": "/api/users?page=1&limit=10" } }, "_embedded": { "items": [ { "id": 123 }, { "id": 456 } ] } }
以及以下XML内容
<?xml version="1.0" encoding="UTF-8"?> <collection page="1" limit="10" pages="1"> <entry id="123"></entry> <entry id="456"></entry> <link rel="self" href="/api/users?page=1&limit=10" /> <link rel="first" href="/api/users?page=1&limit=10" /> <link rel="last" href="/api/users?page=1&limit=10" /> </collection>
如果您想自定义内联的CollectionRepresentation
,请将一个实例作为createRepresentation()
方法的第三个参数传递
use Hateoas\Representation\Factory\PagerfantaFactory; $pagerfantaFactory = new PagerfantaFactory(); // you can pass the page and limit parameters name $paginatedCollection = $pagerfantaFactory->createRepresentation( $pager, new Route('user_list', array()), new CollectionRepresentation($pager->getCurrentPageResults()) ); $json = $hateoas->serialize($paginatedCollection, 'json'); $xml = $hateoas->serialize($paginatedCollection, 'xml');
如果您想更改集合的XML根名称,请创建一个新的类,配置xml根,并使用内联机制
use JMS\Serializer\Annotation as Serializer; /** * @Serializer\XmlRoot("users") */ class UsersRepresentation { /** * @Serializer\Inline */ private $inline; public function __construct($inline) { $this->inline = $inline; } } $paginatedCollection = ...; $paginatedCollection = new UsersRepresentation($paginatedCollection);
表示
如前所述,表示是配置了库的注解的类,旨在帮助您完成常见任务。有关集合表示的更多信息,请参阅处理集合。
VndErrorRepresentation
VndErrorRepresentation
允许您根据vnd.error
规范描述错误响应。
$error = new VndErrorRepresentation( 'Validation failed', 42, 'http://.../', 'http://.../' );
在XML和JSON中序列化此类表示将给出以下输出
<?xml version="1.0" encoding="UTF-8"?> <resource logref="42"> <message><![CDATA[Validation failed]]></message> <link rel="help" href="http://.../"/> <link rel="describes" href="http://.../"/> </resource>
{ "message": "Validation failed", "logref": 42, "_links": { "help": { "href": "http://.../" }, "describes": { "href": "http://.../" } } }
提示:建议您创建自己的错误类,这些类扩展了VndErrorRepresentation
类。
表达式语言
Hateoas依赖于强大的Symfony 表达式语言组件来检索值,如链接、id或要嵌入的对象。
每次您填写一个值(例如,在注解或YAML中传递的链接href
),您可以选择传递一个硬编码值
或一个表达式
。为了使用表达式语言,您必须使用expr()
表示法
/** * @Hateoas\Relation("self", href = "expr('/api/users/' ~ object.getId())") */
您可以通过阅读官方文档了解更多有关表达式语法的信息:表达式语法。
上下文
在默认情况下,每个表达式都可用一个名为object
的特殊变量,并代表当前对象
expr(object.getId())
我们称这样的变量为上下文变量
。
您可以通过将它们添加到表达式评估器中,将您自己的上下文变量添加到表达式语言上下文中
添加自己的上下文变量
使用HateoasBuilder
,调用setExpressionContextVariable()
方法来添加新的上下文变量
use Hateoas\HateoasBuilder; $hateoas = HateoasBuilder::create() ->setExpressionContextVariable('foo', new Foo()) ->build();
foo
变量现在可用
expr(foo !== null)
表达式函数
有关如何向表达式语言添加函数的更多信息,请参阅https://symfony.com/doc/current/components/expression_language/extending.html
URL 生成器
由于您可以使用Expression Language
定义关系链接(href
键),因此默认情况下可以完成很多事情。但是,如果您正在使用框架,那么您很可能会希望使用路由来构建链接。
您首先需要在构建器上配置一个 UrlGenerator
。您可以选择实现 Hateoas\UrlGenerator\UrlGeneratorInterface
,或者使用 Hateoas\UrlGenerator\CallableUrlGenerator
use Hateoas\UrlGenerator\CallableUrlGenerator; $hateoas = HateoasBuilder::create() ->setUrlGenerator( null, // By default all links uses the generator configured with the null name new CallableUrlGenerator(function ($route, array $parameters, $absolute) use ($myFramework) { return $myFramework->generateTheUrl($route, $parameters, $absolute); }) ) ->build() ;
然后您将能够使用 @Route 注解
use Hateoas\Configuration\Annotation as Hateoas; /** * @Hateoas\Relation( * "self", * href = @Hateoas\Route( * "user_get", * parameters = { * "id" = "expr(object.getId())" * } * ) * ) */ class User
{ "id": 42, "first_name": "Adrien", "last_name": "Brault", "_links": { "self": { "href": "/api/users/42" } } }
注意,该库附带了一个 SymfonyUrlGenerator
。例如,在 Silex 中使用它
use Hateoas\UrlGenerator\SymfonyUrlGenerator; $hateoas = HateoasBuilder::create() ->setUrlGenerator(null, new SymfonyUrlGenerator($app['url_generator'])) ->build() ;
辅助工具
Hateoas 提供了一套助手函数,以简化构建 API 的过程。
LinkHelper
LinkHelper
类提供了一个 getLinkHref($object, $rel, $absolute = false)
方法,允许您获取任何对象的 href 值,对于任何给定的关系名称。它能够从任何 链接 关系生成 URI(绝对或相对)
$user = new User(123, 'William', 'Durand'); $linkHelper->getLinkHref($user, 'self'); // /api/users/123 $linkHelper->getLinkHref($user, 'self', true); // http://example.com/api/users/123
link
函数
上述功能也适用于您的表达式(cf. 表达式语言)中的 link(object, rel, absolute)
函数
/** * @Hateoas\Relation( * "self", * href = @Hateoas\Route("post_get", parameters = {"id" = "expr(object.getId())"}) * ) */ class Post {} /** * @Hateoas\Relation( * "self", * href = @Hateoas\Route("user_get", parameters = {"id" = "expr(object.getId())"}) * ) * @Hateoas\Relation( * "post", * href = "expr(link(object.getPost(), 'self', true))" * ) * @Hateoas\Relation( * "relative", * href = "expr(link(object.getRelativePost(), 'self'))" * ) */ class User { ... public function getPost() { return new Post(456); } public function getRelativePost() { return new Post(789); } }
请注意 post
和 relative
关系及其在以下 JSON 内容中的相应值中的 href
表达式
{ "user": { "id": 123, "first_name": "William", "last_name": "Durand", "_links": { "self": { "href": "http://example.com/api/users/123" }, "post": { "href": "http://example.com/api/posts/456" }, "relative": { "href": "/api/posts/789" } } } }
值得提及的是,您可以通过在 getLinkHref()
方法中以及在 link
函数中使用第三个参数来强制选择绝对或相对 URI。
重要:默认情况下,所有 URI 都将是 相对的,即使它们在配置中定义为 绝对。
$linkHelper->getLinkHref($user, 'post'); // /api/posts/456 $linkHelper->getLinkHref($user, 'post', true); // http://example.com/api/posts/456 $linkHelper->getLinkHref($user, 'relative'); // /api/posts/789 $linkHelper->getLinkHref($user, 'relative', true); // http://example.com/api/posts/789
Twig 扩展
Hateoas 还提供了一套 Twig 扩展。
LinkExtension
LinkExtension
允许您将 LinkHelper 用于您的 Twig 模板中,这样您就可以在 HTML 模板中生成链接等。
此扩展通过 link_href
Twig 函数公开了 getLinkHref()
助手函数的方法
{{ link_href(user, 'self') }} {# will generate: /users/123 #} {{ link_href(will, 'self', false) }} {# will generate: /users/123 #} {{ link_href(will, 'self', true) }} {# will generate: http://example.com/users/123 #}
序列化器与格式
Hateoas 提供了一套 序列化器。每个 序列化器 允许您生成遵循特定 格式 的 XML 或 JSON 内容,例如 HAL 或 Atom Links。
JsonHalSerializer
JsonHalSerializer
允许您在 JSON 中生成 HAL 兼容的关系。它是 Hateoas 中的默认 JSON 序列化器。
HAL 提供了其链接功能,约定称资源对象有一个名为 _links
的保留属性。此属性是一个包含链接的对象。这些链接通过它们的链接关系进行键控。
HAL 还描述了另一个约定,称资源可能还有一个名为 _embedded
的保留属性。此属性与 _links
类似,因为嵌入式资源通过关系名称进行键控。主要区别是,而不是链接,值是资源对象。
{ "message": "Hello, World!", "_links": { "self": { "href": "/notes/0" } }, "_embedded": { "associated_events": [ { "name": "SymfonyCon", "date": "2013-12-12T00:00:00+0100" } ] } }
XmlSerializer
XmlSerializer
允许您在 XML 文档中生成 Atom Links。它是默认的 XML 序列化器。
<?xml version="1.0" encoding="UTF-8"?> <note> <message><![CDATA[Hello, World!]]></message> <link rel="self" href="/notes/0" /> <events rel="associated_events"> <event> <name><![CDATA[SymfonyCon]]></name> <date><![CDATA[2013-12-12T00:00:00+0100]]></date> </event> </events> </note>
XmlHalSerializer
XmlHalSerializer
允许您在 XML 中生成 HAL 兼容的关系。
XML 中的 HAL 与 JSON 中的 HAL 类似,因为它描述了 link
标签和 resource
标签。
注意:实际的 self
关系将成为主要资源的属性,而不是 link
标签。其他链接将生成为 link
标签。
<?xml version="1.0" encoding="UTF-8"?> <note href="/notes/0"> <message><![CDATA[Hello, World!]]></message> <resource rel="associated_events"> <name><![CDATA[SymfonyCon]]></name> <date><![CDATA[2013-12-12T00:00:00+0100]]></date> </resource> </note>
添加新的序列化器
您必须实现描述两个方法来序列化 链接 和 嵌入式 关系的 SerializerInterface
。
HateoasBuilder
HateoasBuilder
类用于通过强大的流畅 API 轻松配置 Hateoas。
use Hateoas\HateoasBuilder; $hateoas = HateoasBuilder::create() ->setCacheDir('/path/to/cache/dir') ->setDebug($trueOrFalse) ->setDefaultXmlSerializer() ... ->build();
以下所有方法都返回当前构建器,以便您可以链接它们。
XML 序列化器
setXmlSerializer(SerializerInterface $xmlSerializer)
:设置要使用的XML序列化器。默认为:XmlSerializer
;setDefaultXmlSerializer()
:设置默认的XML序列化器(XmlSerializer
)。
JSON 序列化器
setJsonSerializer(SerializerInterface $jsonSerializer)
:设置要使用的JSON序列化器。默认为:JsonHalSerializer
;setDefaultJsonSerializer()
:设置默认的JSON序列化器(JsonHalSerializer
)。
URL 生成器
setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
:添加一个命名URL生成器。如果$name
为null
,则URL生成器将是默认的。
表达式评估器/表达式语言
setExpressionContextVariable($name, $value)
:添加一个新的表达式上下文变量;setExpressionLanguage(ExpressionLanguage $expressionLanguage)
;
(JMS) 序列化器特定
includeInterfaceMetadata($include)
:是否包含接口的元数据;setMetadataDirs(array $namespacePrefixToDirMap)
:设置命名空间前缀到目录的映射。此方法覆盖了先前定义的目录;addMetadataDir($dir, $namespacePrefix = '')
:添加一个序列化器将查找类元数据的目录;addMetadataDirs(array $namespacePrefixToDirMap)
:添加命名空间前缀到目录的映射;replaceMetadataDir($dir, $namespacePrefix = '')
:与addMetadataDir()
类似,但覆盖现有条目。
请阅读官方的序列化器文档以获取更多信息。
其他
setDebug($debug)
:启用或禁用调试模式;setCacheDir($dir)
:设置缓存目录。
配置缓存目录
序列化器和Hateoas库会从各种来源(如YML、XML或注解)收集您的对象的元数据。为了使此过程尽可能高效,建议您允许Hateoas库缓存此信息。为此,配置一个缓存目录
$builder = \Hateoas\HateoasBuilder::create(); $hateoas = $builder ->setCacheDir($someWritableDir) ->build();
配置元数据位置
Hateoas支持多个元数据来源。默认情况下,它使用Doctrine注解,但您也可以将元数据存储在XML或YAML文件中。对于后者,有必要配置一个元数据目录,其中包含这些文件
$hateoas = \Hateoas\HateoasBuilder::create() ->addMetadataDir($someDir) ->build();
Hateoas期望元数据文件以完全限定的类名命名,其中所有\
都替换为.
。如果您的类名为Vendor\Package\Foo
,则元数据文件需要位于$someDir/Vendor.Package.Foo.(xml|yml)
。
扩展库
Hateoas允许框架在配置级别动态添加到类的关系,通过提供一个配置级别的扩展点。此功能对于希望在Hateoas之上创建新层或添加“全局”关系而不是在每个类上复制相同的配置的人来说非常有用。
为了利用此机制,必须实现ConfigurationExtensionInterface
接口
use Hateoas\Configuration\Metadata\ConfigurationExtensionInterface; use Hateoas\Configuration\Metadata\ClassMetadataInterface; use Hateoas\Configuration\Relation; class AcmeFooConfigurationExtension implements ConfigurationExtensionInterface { /** * {@inheritDoc} */ public function decorate(ClassMetadataInterface $classMetadata): void { if (0 === strpos('Acme\Foo\Model', $classMetadata->getName())) { // Add a "root" relation to all classes in the `Acme\Foo\Model` namespace $classMetadata->addRelation( new Relation( 'root', '/' ) ); } } }
您可以使用$classMetadata->getRelations()
访问从注解、XML或YAML加载的现有关系。
如果$classMetadata
有关系,或您向其中添加关系,其关系将被缓存。因此,如果您读取配置文件(注解、XML或YAML),请确保在类元数据中引用它们
$classMetadata->fileResources[] = $file;
参考
XML
<?xml version="1.0" encoding="UTF-8"?> <serializer> <class name="Acme\Demo\Representation\User" h:providers="Class::getRelations expr(sevice('foo').getMyAdditionalRelations())" xmlns:h="https://github.com/willdurand/Hateoas"> <h:relation rel="self"> <h:href uri="http://acme.com/foo/1" /> </h:relation> <h:relation rel="friends"> <h:href route="user_friends" generator="my_custom_generator"> <h:parameter name="id" value="expr(object.getId())" /> <h:parameter name="page" value="1" /> </h:ref> <h:embedded xml-element-name="users"> <h:content>expr(object.getFriends())</h:content> <h:exclusion ... /> </h:embedded> <h:exclusion groups="Default, user_full" since-version="1.0" until-version="2.2" exclude-if="expr(object.getFriends() === null)" /> </h:relation> </class> </serializer>
有关详细信息,请参阅hateoas.xsd
文件。
YAML
Acme\Demo\Representation\User: relations: - rel: self href: http://acme.com/foo/1 - rel: friends href: route: user_friends parameters: id: expr(object.getId()) page: 1 generator: my_custom_generator absolute: false embedded: content: expr(object.getFriends()) xmlElementName: users exclusion: ... exclusion: groups: [Default, user_full] since_version: 1.0 until_version: 2.2 exclude_if: expr(object.getFriends() === null) relation_providers: [ "Class::getRelations", "expr(sevice('foo').getMyAdditionalRelations())" ]
注解
@Relation
此注解可以定义在类上。
use Hateoas\Configuration\Annotation as Hateoas; /** * @Hateoas\Relation( * name = "self", * href = "http://hello", * embedded = "expr(object.getHello())", * attributes = { "foo" = "bar" }, * exclusion = ..., * ) */
重要: attributes
仅在链接关系上使用(即与href
属性结合,而不是与embedded
属性结合)。
@Route
use Hateoas\Configuration\Annotation as Hateoas; /** * @Hateoas\Relation( * name = "self", * href = @Hateoas\Route( * "user_get", * parameters = { "id" = "expr(object.getId())" }, * absolute = true, * generator = "my_custom_generator" * ) * ) */
此注解可以定义在@Relation注解的href
属性中。这允许您使用URL生成器,如果您已配置了它。
@Embedded
use Hateoas\Configuration\Annotation as Hateoas; /** * @Hateoas\Relation( * name = "friends", * embedded = @Hateoas\Embedded( * "expr(object.getFriends())", * exclusion = ..., * xmlElementName = "users" * ) * ) */
此注解可以定义在@Relation注解的embedded
属性中。如果您需要配置嵌入式资源的exclusion
或xmlElementName
选项,则非常有用。
@Exclusion
此注解可以定义在@Relation和@Embedded注解的exclusion
属性中。
除了excludeIf
之外的所有值在序列化器直接应用于常规属性时表现相同。
excludeIf
期望一个布尔值,在某种情况下其他表达式可能失败时很有用。在这个例子中,如果getManager
方法是null
,应该排除它以防止URL生成失败。
/** * @Hateoas\Relation( * "manager", * href = @Hateoas\Route( * "user_get", * parameters = { "id" = "expr(object.getManager().getId())" } * ), * exclusion = @Hateoas\Exclusion(excludeIf = "expr(null === object.getManager())") * ) */ class User { public function getId() {} /** * @return User|null */ public function getManager() {} }
@RelationProvider
这个注解可以定义在类上。如果你希望序列化多关系(链接),这将很有用。例如
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}
可以是"名称"
- 一个函数:
my_func
- 一个静态方法:
MyClass::getExtraRelations
- 一个表达式:
expr(service('user.rel_provider').getExtraRelations())
这里有一个使用表达式语言的示例
use Hateoas\Configuration\Annotation as Hateoas; /** * @Hateoas\RelationProvider("expr(service('user.rel_provider').getExtraRelations())") */ class User { ... }
这里使用的是UserRelPrvider
类
use Hateoas\Configuration\Relation; use Hateoas\Configuration\Route; class UserRelPrvider { private $evaluator; public function __construct(CompilableExpressionEvaluatorInterface $evaluator) { $this->evaluator = $evaluator; } /** * @return Relation[] */ public function getExtraRelations(): array { // You need to return the relations return array( new Relation( 'self', new Route( 'foo_get', ['id' => $this->evaluator->parse('object.getId()', ['object'])] ) ) ); } }
$this->evaluator
实现CompilableExpressionEvaluatorInterface
用于解析可以缓存和供以后使用的表达式语言的形式。如果你在你的关系不需要表达式语言,则不需要此服务。
user.rel_provider
服务定义如下
user.rel_provider: class: UserRelPrvider arguments: - '@jms_serializer.expression_evaluator'
在这种情况下jms_serializer.expression_evaluator
是一个实现CompilableExpressionEvaluatorInterface
的服务。
内部
本节涉及Hateoas的内部结构,提供了关于此库隐藏部分的文档。这并不总是对最终用户相关,但对于开发者或对了解事情底层工作原理感兴趣的人来说很有趣。
版本控制
willdurand/hateoas
遵循语义版本控制。
生命周期结束
截至2013年10月,版本1.x
和0.x
已不再官方支持(注意1.x
从未发布)。
稳定版本
版本3.x
是当前的主要稳定版本。
版本2.x
仅维护用于安全漏洞修复和可能出现的重大问题。
贡献
请参阅CONTRIBUTING文件。
运行测试
安装Composer dev
依赖项
php composer.phar install --dev
然后,使用PHPUnit运行测试套件
bin/phpunit
许可证
Hateoas在MIT许可证下发布。有关详细信息,请参阅捆绑的LICENSE文件。