Библиотека PHP для поддержки реализации представлений веб-сервисов HATEOAS REST.
Рекомендуемый способ установки Hateoas — через Composer. Потребуйте пакет willdurand/hateoas
выполнив следующую команду:
composer require willdurand/hateoas
Это решит последнюю стабильную версию.
В противном случае установите библиотеку и настройте автозагрузчик самостоятельно.
Если вы хотите использовать аннотации для конфигурации, вам необходимо установить пакет doctrine/annotations
:
composer require doctrine/annotations
Если ваше приложение использует PHP 8.1 или выше, рекомендуется использовать собственные атрибуты PHP. В этом случае вам не нужно устанавливать пакет Doctrine.
Для этого есть комплект! Установите BazingaHateoasBundle и наслаждайтесь!
Важный:
Те, кто использует версию
1.0
, могут перейти на эту страницу документации.Те, кто использует версию
2.0
, могут перейти на эту страницу документации.Следующая документация написана для Hateas 3.0 и выше.
Hateoas использует библиотеку Serializer, чтобы обеспечить удобный способ создания веб-сервисов HATEOAS REST. HATEOAS означает «Гипермедиа как механизм состояния приложения » и добавляет гипермедийные ссылки к вашим представлениям (т. е. вашим ответам API). HATEOAS — это возможность обнаружения действий на ресурсе.
Например, предположим, что у вас есть User 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 " }
}
}
}
Давайте теперь углубимся в Хатеоаса.
В терминологии Хатеоа ссылки рассматриваются как отношения, добавленные к ресурсам. Стоит отметить, что отношения также относятся и к встроенным ресурсам , но эта тема будет рассмотрена в разделе «Внедрение ресурсов».
Ссылка — это отношение, которое идентифицируется 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 () {}
}
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.
Важно: вы должны настроить Serializer и Hateoas одинаково. Например, если вы используете YAML для настройки сериализатора, используйте YAML для настройки Hateoas.
Самый простой способ попробовать HATEOAS — использовать HateoasBuilder
. У компоновщика есть множество методов настройки сериализатора Hateoas, но мы не будем сейчас в них углубляться (см. The 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
является экземпляром JMSSerializerSerializerInterface
, взятым из библиотеки Serializer. Hateoas не имеет собственного сериализатора, он подключается к сериализатору JMS.
По умолчанию Hateoas использует язык гипертекстовых приложений (HAL) для сериализации JSON. Это определяет структуру ответа (например, что «ссылки» должны находиться под ключом _links
):
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
Для XML по умолчанию используются ссылки Atom:
< 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 ;
}
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: new Hateoas Exclusion (excludeif: " expr(object.getManager() === null) " ),
)]
class User
{
...
#[ Serializer Exclude]
private $ manager ;
}
Примечание. Вам нужно будет исключить свойство менеджера из сериализации, иначе его сериализуют и сериализатор, и Hateas. Вам также придется исключить отношение менеджера, если менеджер имеет null
, поскольку в противном случае при создании ссылки href
(вызов getId()
для null
) произойдет ошибка.
Совет: Если свойство менеджера является объектом, у которого уже есть ссылка _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
( xml_root_name
в YAML, xml-root-name
в XML), полученной из конфигурации сериализатора.
Библиотека предоставляет несколько классов в пространстве имен HateoasRepresentation*
которые помогут вам в решении типичных задач. Это простые классы, настроенные с помощью аннотаций библиотеки.
Классы PaginatedRepresentation
, 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
на основе заданного маршрута.
Вы можете генерировать абсолютные URI , установив для absolute
параметра значение true
как в PaginatedRepresentation
, так и в RouteAwareRepresentation
.
Библиотека Hateoas также предоставляет PagerfantaFactory
для простой сборки PaginatedRepresentation
из экземпляра Pagerfanta. Если вы используете библиотеку 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 );
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
позволяет описать ответ на ошибку в соответствии со спецификацией 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 ExpressionLanguage для получения таких значений, как ссылки, идентификаторы или объекты для встраивания.
Каждый раз, когда вы заполняете значение (например, href
отношение в аннотациях или YAML), вы можете передать либо жестко запрограммированное значение , либо выражение . Чтобы использовать язык выражений, вам необходимо использовать нотацию expr()
:
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation ( "self" , href = "expr('/api/users/' ~ object.getId())" )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ 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/comComponents/expression_language/extending.html.
Поскольку вы можете использовать язык выражений для определения связей отношений (ключ href
), по умолчанию вы можете многое сделать. Однако если вы используете фреймворк, скорее всего, вы захотите использовать маршруты для построения ссылок.
Сначала вам нужно будет настроить UrlGenerator
в конструкторе. Вы можете либо реализовать HateoasUrlGeneratorUrlGeneratorInterface
, либо использовать HateoasUrlGeneratorCallableUrlGenerator
:
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
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
' self ' ,
href: new 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
предоставляет метод 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
Вышеупомянутая функция также доступна в ваших выражениях (см. Язык выражений) через функцию 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 );
}
}
#[ Hateoas Relation(
' self ' ,
href: new Hateoas Route (
' post_get ' ,
parameters: [
' id ' => ' expr(object.getId()) ' ,
],
),
)]
class Post {}
#[ Hateoas Relation(
' self ' ,
href: new 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 );
}
}
Обратите внимание на выражения href
для post
и relative
отношений, а также соответствующие им значения в следующем содержимом JSON:
{
"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 " }
}
}
}
Стоит отметить, что вы можете указать , хотите ли вы использовать абсолютный или относительный URI, используя третий аргумент как в методе getLinkHref()
, так и в функции link
.
Важно: по умолчанию все 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
Hateoas также предоставляет набор расширений Twig.
LinkExtension
позволяет вам использовать LinkHelper в ваших шаблонах Twig, чтобы вы могли, например, создавать ссылки в своих HTML-шаблонах.
Это расширение предоставляет вспомогательный метод getLinkHref()
через функцию link_href
Twig:
{{ 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
позволяет создавать HAL-совместимые отношения в JSON. Это сериализатор JSON по умолчанию в Hateoas.
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
позволяет создавать ссылки Atom в ваших XML-документах. Это сериализатор 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
позволяет создавать в XML отношения, совместимые с HAL.
HAL в XML аналогичен HAL в JSON в том смысле, что он описывает теги 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
используется для простой настройки Hateoas благодаря мощному и гибкому API.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setCacheDir ( ' /path/to/cache/dir ' )
-> setDebug ( $ trueOrFalse )
-> setDefaultXmlSerializer ()
. . .
-> build ();
Все приведенные ниже методы возвращают текущего построителя, чтобы вы могли объединить их.
setXmlSerializer(SerializerInterface $xmlSerializer)
: устанавливает используемый сериализатор XML. По умолчанию: XmlSerializer
;setDefaultXmlSerializer()
: устанавливает сериализатор XML по умолчанию ( XmlSerializer
). setJsonSerializer(SerializerInterface $jsonSerializer)
: устанавливает используемый сериализатор JSON. По умолчанию: JsonHalSerializer
;setDefaultJsonSerializer()
: устанавливает сериализатор JSON по умолчанию ( JsonHalSerializer
). setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
: добавляет новый именованный генератор URL-адресов. Если $name
имеет null
, генератор URL-адресов будет использоваться по умолчанию. setExpressionContextVariable($name, $value)
: добавляет новую переменную контекста выражения;setExpressionLanguage(ExpressionLanguage $expressionLanguage)
; includeInterfaceMetadata($include)
: включать ли метаданные из интерфейсов;setMetadataDirs(array $namespacePrefixToDirMap)
: устанавливает карту префиксов пространства имен для каталогов. Этот метод переопределяет любые ранее определенные каталоги;addMetadataDir($dir, $namespacePrefix = '')
: добавляет каталог, в котором сериализатор будет искать метаданные класса;addMetadataDirs(array $namespacePrefixToDirMap)
: добавляет в каталоги карту префиксов пространства имен;replaceMetadataDir($dir, $namespacePrefix = '')
: аналогично addMetadataDir()
, но переопределяет существующую запись.Пожалуйста, прочитайте официальную документацию сериализатора для более подробной информации.
setDebug($debug)
: включает или отключает режим отладки;setCacheDir($dir)
: устанавливает каталог кэша.И сериализатор, и библиотеки Hateas собирают метаданные о ваших объектах из различных источников, таких как YML, XML или аннотации. Чтобы сделать этот процесс максимально эффективным, рекомендуется разрешить библиотеке Hateoas кэшировать эту информацию. Для этого настройте каталог кэша:
$ builder = Hateoas HateoasBuilder:: create ();
$ hateoas = $ builder
-> setCacheDir ( $ someWritableDir )
-> build ();
Hateoas поддерживает несколько источников метаданных. По умолчанию он использует аннотации Doctrine (PHP < 8.1) или собственные атрибуты PHP (PHP >= 8.1), но вы также можете хранить метаданные в файлах XML или YAML. Для последнего необходимо настроить каталог метаданных, в котором расположены эти файлы:
$ hateoas = Hateoas HateoasBuilder:: create ()
-> addMetadataDir ( $ someDir )
-> build ();
Хатеоас ожидал, что файлы метаданных будут называться как полные имена классов, где все будут заменены на
.
. Если ваш класс будет называться VendorPackageFoo
файл метаданных должен находиться по адресу $someDir/Vendor.Package.Foo.(xml|yml)
.
Hateas позволяет платформам динамически добавлять отношения к классам, предоставляя точку расширения на уровне конфигурации. Эта функция может быть полезна для тех, кто хочет создать новый слой поверх 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 ( ' AcmeFooModel ' , $ classMetadata -> getName ())) {
// Add a "root" relation to all classes in the `AcmeFooModel` namespace
$ classMetadata -> addRelation (
new Relation (
' root ' ,
' / '
)
);
}
}
}
Вы можете получить доступ к существующим отношениям, загруженным из аннотаций, XML или YAML, с помощью $classMetadata->getRelations()
.
Если $classMetadata
имеет отношения или вы добавляете к нему отношения, их отношения будут кэшироваться. Поэтому, если вы читаете файлы конфигурации (аннотации, XML или YAML), обязательно ссылайтесь на них в метаданных класса:
$ classMetadata -> fileResources [] = $ file ;
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< serializer >
< class name = " AcmeDemoRepresentationUser " 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
.
AcmeDemoRepresentationUser :
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())" ]
Эту аннотацию можно определить в классе.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* name = "self" ,
* href = "http://hello" ,
* embedded = "expr(object.getHello())" ,
* attributes = { "foo" = "bar" },
* exclusion = ...,
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' self ' ,
href: ' http://hello ' ,
embedded: ' expr(object.getHello()) ' ,
attributes: [ ' foo ' => ' bar ' ],
exclusion: ' ... ' ,
)]
Свойство | Необходимый | Содержание | Язык выражений |
---|---|---|---|
имя | Да | нить | Нет |
href | Если встроенный не установлен | строка / @Route | Да |
встроенный | Если href не установлен | строка / @Embedded | Да |
атрибуты | Нет | множество | Да, по ценностям |
исключение | Нет | @Исключение | Н/Д |
Важно: attributes
используются только в отношениях ссылок (т. е. в сочетании со свойством href
, а не со embedded
).
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"
* )
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' self ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [ ' id ' = ' expr (object. getId ())'],
absolute: true ,
generator: ' my_custom_generator ' ,
),
)]
Эту аннотацию можно определить в свойстве href аннотации @Relation. Это позволит вам использовать генератор URL-адресов, если вы его настроили.
Свойство | Необходимый | Содержание | Язык выражений |
---|---|---|---|
имя | Да | нить | Нет |
параметры | По умолчанию массив() | массив/строка | Да (строка + значения массива) |
абсолютный | По умолчанию ложь | логическое значение/строка | Да |
генератор | Нет | строка/ноль | Нет |
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* name = "friends" ,
* embedded = @ Hateoas Embedded (
* "expr(object.getFriends())" ,
* exclusion = ...,
* xmlElementName = "users"
* )
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' friends ' ,
embedded: new Hateoas Embedded (
' expr(object.getFriends()) ' ,
exclusion: ' ... ' ,
xmlElementName: ' users ' ,
),
)]
Эту аннотацию можно определить во встроенном свойстве аннотации @Relation. Это полезно, если вам нужно настроить параметры exclusion
или xmlElementName
для внедренного ресурса.
Свойство | Необходимый | Содержание | Язык выражений |
---|---|---|---|
содержание | Да | строка/массив | Да (строка) |
исключение | По умолчанию массив() | @Исключение | Н/Д |
xmlElementName | По умолчанию массив() | нить | Нет |
Эту аннотацию можно определить в свойстве исключения аннотаций @Relation и @Embedded.
Свойство | Необходимый | Содержание | Язык выражений |
---|---|---|---|
группы | Нет | множество | Нет |
начиная с версии | Нет | нить | Нет |
до версии | Нет | нить | Нет |
максимальная глубина | Нет | целое число | Нет |
исключитьЕсли | Нет | строка/логическое значение | Да |
Все значения, кроме excludeIf
действуют так же, как если бы они использовались непосредственно в обычных свойствах сериализатора.
excludeIf
ожидает логическое значение и полезен, когда другое выражение при некоторых обстоятельствах не работает. В этом примере, если метод getManager
имеет null
, вам следует исключить его, чтобы предотвратить сбой при создании URL-адреса:
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ 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 () {}
}
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' manager ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [ ' id ' => ' expr(object.getManager().getId()) ' ],
),
exclusion: new Hateoas Exclusion (excludeIf: ' expr(null === object.getManager()) ' )
)]
class User
{
public function getId () {}
public function getManager (): ? User {}
}
Эту аннотацию можно определить в классе. Это полезно, если вы хотите сериализовать множественные отношения (ссылки). В качестве примера:
{
"_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
{
...
}
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 года версии 1.x
и 0.x
официально больше не поддерживаются (обратите внимание, что 1.x
так и не была выпущена).
Версия 3.x
является текущей основной стабильной версией.
Версия 2.x
поддерживается только для исправления ошибок безопасности и серьезных проблем, которые могут возникнуть.
См. файл «ВКЛАД».
Установите зависимости dev
Composer:
php composer.phar install --dev
Затем запустите набор тестов с помощью PHPUnit:
bin/phpunit
Hateoas выпускается под лицензией MIT. Подробности смотрите в прилагаемом файле ЛИЦЕНЗИИ.