Uma biblioteca PHP para suportar a implementação de representações para serviços web HATEOAS REST.
A forma recomendada de instalar o Hateoas é através do Composer. Solicite o pacote willdurand/hateoas
executando o seguinte comando:
composer require willdurand/hateoas
Isso resolverá a versão estável mais recente.
Caso contrário, instale a biblioteca e configure o autoloader você mesmo.
Se você quiser usar anotações para configuração você precisa instalar o pacote doctrine/annotations
:
composer require doctrine/annotations
Se seu aplicativo usa PHP 8.1 ou superior, é recomendado usar atributos nativos do PHP. Neste caso você não precisa instalar o pacote Doctrine.
Existe um pacote para isso! Instale o BazingaHateoasBundle e divirta-se!
Importante:
Para quem usa a versão
1.0
, você pode pular para esta página de documentação.Para quem usa a versão
2.0
, você pode pular para esta página de documentação.A seguinte documentação foi escrita para Hateoas 3.0 e superior.
Hateoas aproveita a biblioteca Serializer para fornecer uma ótima maneira de construir serviços web HATEOAS REST. HATEOAS significa Hypermedia as the Engine of Application State e adiciona links de hipermídia às suas representações (ou seja, suas respostas da API). HATEOAS trata da descoberta de ações em um recurso.
Por exemplo, digamos que você tenha uma API de usuário que retorna uma representação de um único usuário como segue:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe "
}
}
Para informar aos consumidores da API como recuperar os dados desse usuário específico, você deve adicionar seu primeiro link a essa representação, vamos chamá-lo de self
, pois é o URI desse usuário específico:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe " ,
"_links" : {
"self" : { "href" : " http://example.com/api/users/123 " }
}
}
}
Vamos nos aprofundar em Hateoas agora.
Na terminologia Hateoas, os links são vistos como relações adicionadas aos recursos. Vale ressaltar que as relações também se referem a recursos incorporados , mas este tópico será abordado na seção Incorporando Recursos.
Um link é uma relação identificada por um name
(por exemplo, self
) e que possui um parâmetro 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 () {}
}
No exemplo acima, configuramos uma relação self
que é um link por causa do parâmetro href
. Seu valor, que pode parecer estranho à primeira vista, será amplamente abordado na seção The Expression Language. Este valor especial é usado para gerar um URI.
Nesta seção, anotações/atributos são usados para configurar o Hateoas. Os formatos XML e YAML também são suportados. Se desejar, você também pode usar PHP simples.
Importante: você deve configurar o Serializer e o Hateoas da mesma forma. Por exemplo, se você usa YAML para configurar o Serializer, use YAML para configurar o Hateoas.
A maneira mais fácil de experimentar o HATEOAS é com o HateoasBuilder
. O construtor tem vários métodos para configurar o serializador Hateoas, mas não vamos nos aprofundar neles agora (veja O HateoasBuilder). Tudo funciona bem imediatamente:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()-> build ();
$ user = new User ( 42 , ' Adrien ' , ' Brault ' );
$ json = $ hateoas -> serialize ( $ user , ' json ' );
$ xml = $ hateoas -> serialize ( $ user , ' xml ' );
O objeto $hateoas
é uma instância de JMSSerializerSerializerInterface
, proveniente da biblioteca Serializer. Hateoas não vem com seu próprio serializador, ele se conecta ao JMS Serializer.
Por padrão, Hateoas usa Hypertext Application Language (HAL) para serialização JSON. Isto especifica a estrutura da resposta (por exemplo, que "links" devem estar sob uma chave _links
):
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
Para XML, os links Atom são usados por padrão:
< user id = " 42 " >
< first_name > <![CDATA[ Adrien ]]> </ first_name >
< last_name > <![CDATA[ Brault ]]> </ last_name >
< link rel = " self " href = " /api/users/42 " />
</ user >
Vale ressaltar que esses formatos são os padrão e não os únicos disponíveis. Você pode usar diferentes formatos através de diferentes serializadores e até mesmo adicionar os seus próprios.
Agora que você sabe como adicionar links , vamos ver como adicionar recursos incorporados .
Às vezes, é mais eficiente incorporar recursos relacionados em vez de vinculá-los, pois evita que os clientes tenham que fazer solicitações extras para buscar esses recursos.
Um recurso incorporado é uma relação nomeada que contém dados, representados pelo parâmetro 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 ;
}
Nota: Você precisará excluir a propriedade manager da serialização, caso contrário, tanto o serializador quanto o Hateoas irão serializá-la. Você também terá que excluir a relação do gerente quando o gerente for null
, caso contrário ocorrerá um erro ao criar o link href
(chamando getId()
em null
).
Dica: Se a propriedade manager for um objeto que já possui um link _self
, você poderá reutilizar esse valor para o href
em vez de repeti-lo aqui. ConsulteLinkHelper.
$ 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 ' );
Para json
, a representação HAL coloca essas relações incorporadas dentro de uma chave _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 "
}
}
}
}
}
Em XML, a serialização de relações embedded
criará novos elementos:
< 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 >
O nome da tag de um recurso incorporado é inferido da anotação @XmlRoot
( xml_root_name
em YAML, xml-root-name
em XML) proveniente da configuração do Serializer.
A biblioteca fornece diversas classes no namespace HateoasRepresentation*
para ajudá-lo em tarefas comuns. São classes simples configuradas com as anotações da biblioteca.
As classes PaginatedRepresentation
, OffsetRepresentation
e CollectionRepresentation
são provavelmente as mais interessantes. Eles são úteis quando seu recurso é na verdade uma coleção de recursos (por exemplo, /users
é uma coleção de usuários). Isso ajuda você a representar a coleção e adicionar paginação e limites:
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 ' );
O CollectionRepresentation
oferece uma representação básica de uma coleção incorporada.
O PaginatedRepresentation
foi projetado para adicionar links self
, first
e, quando possível, last
, next
e previous
.
O OffsetRepresentation
funciona exatamente como PaginatedRepresentation
, mas é útil quando a paginação é expressa por offset
, limit
e total
.
O RouteAwareRepresentation
adiciona uma relação self
com base em uma determinada rota.
Você pode gerar URIs absolutos definindo o parâmetro absolute
como true
em PaginatedRepresentation
e RouteAwareRepresentation
.
A biblioteca Hateoas também fornece um PagerfantaFactory
para construir facilmente PaginatedRepresentation
a partir de uma instância do Pagerfanta. Se você usar a biblioteca Pagerfanta, esta é uma maneira mais fácil de criar as representações da coleção:
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 ' );
Você obteria o seguinte conteúdo 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 }
]
}
}
E o seguinte conteúdo 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 >
Se você deseja personalizar o CollectionRepresentation
embutido, passe um como terceiro argumento do método 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 ' );
Se você quiser alterar o nome raiz xml da coleção, crie uma nova classe com a raiz xml configurada e use o mecanismo inline:
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 );
Conforme mencionado na seção anterior, representações são classes configuradas com as anotações da biblioteca para ajudá-lo em tarefas comuns. As representações da coleção são descritas em Lidando com Coleções.
O VndErrorRepresentation
permite descrever uma resposta de erro seguindo a especificação vnd.error
.
$ error = new VndErrorRepresentation (
' Validation failed ' ,
42 ,
' http://.../ ' ,
' http://.../ '
);
Serializar tal representação em XML e JSON forneceria os seguintes resultados:
<? 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://.../ "
}
}
}
Dica: é recomendado criar suas próprias classes de erro que estendam a classe VndErrorRepresentation
.
Hateoas depende do poderoso componente Symfony ExpressionLanguage para recuperar valores como links, ids ou objetos para incorporar.
Cada vez que você preenche um valor (por exemplo, um Relation href
em anotações ou YAML), você pode passar um valor codificado ou uma expressão . Para usar a Expression Language, você deve usar a notação 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()) " )]
Você pode aprender mais sobre a sintaxe de expressão lendo a documentação oficial: The Expression Syntax.
Nativamente, uma variável especial chamada object
está disponível em cada expressão e representa o objeto atual:
expr(object.getId())
Chamamos essa variável de variável de contexto .
Você pode adicionar suas próprias variáveis de contexto ao contexto da Expression Language adicionando-as ao avaliador de expressão.
Usando o HateoasBuilder
, chame o método setExpressionContextVariable()
para adicionar novas variáveis de contexto:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setExpressionContextVariable ( ' foo ' , new Foo ())
-> build ();
A variável foo
agora está disponível:
expr(foo !== null)
Para obter mais informações sobre como adicionar funções à linguagem de expressão, consulte https://symfony.com/doc/current/components/expression_language/extending.html
Como você pode usar a Expression Language para definir os links de relações (chave href
), você pode fazer muito por padrão. No entanto, se você estiver usando uma estrutura, é provável que queira usar rotas para construir links.
Primeiro você precisará configurar um UrlGenerator
no construtor. Você pode implementar o HateoasUrlGeneratorUrlGeneratorInterface
ou usar o 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 ()
;
Você poderá então usar a anotação @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 "
}
}
}
Observe que a biblioteca vem com um SymfonyUrlGenerator
. Por exemplo, para usá-lo no Silex:
use Hateoas UrlGenerator SymfonyUrlGenerator ;
$ hateoas = HateoasBuilder:: create ()
-> setUrlGenerator ( null , new SymfonyUrlGenerator ( $ app [ ' url_generator ' ]))
-> build ()
;
Hateoas fornece um conjunto de ajudantes para facilitar o processo de construção de APIs.
A classe LinkHelper
fornece um método getLinkHref($object, $rel, $absolute = false)
que permite obter o valor href de qualquer objeto, para qualquer nome de relação fornecido. É capaz de gerar um URI (absoluto ou relativo) a partir de qualquer relação de link :
$ 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
O recurso acima também está disponível em suas expressões (cf. The Expression Language) através da função 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 );
}
}
Preste atenção às expressões href
para as relações post
e relative
, bem como seus valores correspondentes no seguinte conteúdo 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 " }
}
}
}
Vale ressaltar que você pode forçar se deseja um URI absoluto ou relativo usando o terceiro argumento no método getLinkHref()
e na função link
.
Importante: por padrão, todos os URIs serão relativos , mesmo aqueles que estão definidos como absolutos em sua configuração.
$ 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 também fornece um conjunto de extensões Twig.
O LinkExtension
permite que você use o LinkHelper em seus modelos Twig, para que você possa gerar links em seus modelos HTML, por exemplo.
Esta extensão expõe o método auxiliar getLinkHref()
por meio da função 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 fornece um conjunto de serializadores . Cada serializador permite gerar conteúdo XML ou JSON seguindo um formato específico, como HAL ou Atom Links, por exemplo.
O JsonHalSerializer
permite gerar relações compatíveis com HAL em JSON. É o serializador JSON padrão no Hateoas.
HAL fornece sua capacidade de vinculação com uma convenção que diz que um objeto de recurso possui uma propriedade reservada chamada _links
. Esta propriedade é um objeto que contém links. Esses links são codificados por sua relação de link.
HAL também descreve outra convenção que diz que um recurso pode ter outra propriedade reservada chamada _embedded
. Esta propriedade é semelhante a _links
em que os recursos incorporados são codificados pelo nome da relação. A principal diferença é que em vez de serem links, os valores são objetos de recursos.
{
"message" : " Hello, World! " ,
"_links" : {
"self" : {
"href" : " /notes/0 "
}
},
"_embedded" : {
"associated_events" : [
{
"name" : " SymfonyCon " ,
"date" : " 2013-12-12T00:00:00+0100 "
}
]
}
}
O XmlSerializer
permite gerar links Atom em seus documentos XML. É o serializador XML padrão.
<? 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 >
O XmlHalSerializer
permite gerar relações compatíveis com HAL em XML.
HAL em XML é semelhante a HAL em JSON no sentido de que descreve tags link
e tags resource
.
Nota: a self
-relação se tornará, na verdade, um atributo do recurso principal em vez de ser uma tag link
. Outros links serão gerados como tags 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 >
Você deve implementar o SerializerInterface
que descreve dois métodos para serializar links e relações incorporadas .
A classe HateoasBuilder
é usada para configurar facilmente o Hateoas graças a uma API poderosa e fluente.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setCacheDir ( ' /path/to/cache/dir ' )
-> setDebug ( $ trueOrFalse )
-> setDefaultXmlSerializer ()
. . .
-> build ();
Todos os métodos abaixo retornam o construtor atual, para que você possa encadeá-los.
setXmlSerializer(SerializerInterface $xmlSerializer)
: define o serializador XML a ser usado. O padrão é: XmlSerializer
;setDefaultXmlSerializer()
: define o serializador XML padrão ( XmlSerializer
). setJsonSerializer(SerializerInterface $jsonSerializer)
: define o serializador JSON a ser usado. O padrão é: JsonHalSerializer
;setDefaultJsonSerializer()
: define o serializador JSON padrão ( JsonHalSerializer
). setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
: adiciona um novo gerador de URL nomeado. Se $name
for null
, o gerador de URL será o padrão. setExpressionContextVariable($name, $value)
: adiciona uma nova variável de contexto de expressão;setExpressionLanguage(ExpressionLanguage $expressionLanguage)
; includeInterfaceMetadata($include)
: se deseja incluir os metadados das interfaces;setMetadataDirs(array $namespacePrefixToDirMap)
: define um mapa de prefixos de namespace para diretórios. Este método substitui quaisquer diretórios definidos anteriormente;addMetadataDir($dir, $namespacePrefix = '')
: adiciona um diretório onde o serializador procurará os metadados da classe;addMetadataDirs(array $namespacePrefixToDirMap)
: adiciona um mapa de prefixos de namespace aos diretórios;replaceMetadataDir($dir, $namespacePrefix = '')
: semelhante a addMetadataDir()
, mas substitui uma entrada existente.Por favor, leia a documentação oficial do Serializer para mais detalhes.
setDebug($debug)
: ativa ou desativa o modo de depuração;setCacheDir($dir)
: define o diretório de cache.Tanto o serializador quanto as bibliotecas Hateoas coletam metadados sobre seus objetos de várias fontes, como YML, XML ou anotações. Para tornar este processo o mais eficiente possível, é recomendado que você permita que a biblioteca Hateoas armazene esta informação em cache. Para fazer isso, configure um diretório de cache:
$ builder = Hateoas HateoasBuilder:: create ();
$ hateoas = $ builder
-> setCacheDir ( $ someWritableDir )
-> build ();
Hateoas oferece suporte a várias fontes de metadados. Por padrão, ele usa anotações do Doctrine (PHP < 8.1) ou atributos nativos do PHP (PHP >= 8.1), mas você também pode armazenar metadados em arquivos XML ou YAML. Para este último, é necessário configurar um diretório de metadados onde estão localizados esses arquivos:
$ hateoas = Hateoas HateoasBuilder:: create ()
-> addMetadataDir ( $ someDir )
-> build ();
Hateoas esperaria que os arquivos de metadados fossem nomeados como nomes de classes totalmente qualificados, onde todos são substituídos por
.
. Se sua classe se chamasse VendorPackageFoo
o arquivo de metadados precisaria estar localizado em $someDir/Vendor.Package.Foo.(xml|yml)
.
Hateoas permite que frameworks adicionem relações às classes de forma dinâmica, fornecendo um ponto de extensão no nível de configuração. Este recurso pode ser útil para quem deseja criar uma nova camada em cima do Hateoas, ou adicionar relações "globais" ao invés de copiar a mesma configuração em cada classe.
Para aproveitar este mecanismo, a interface ConfigurationExtensionInterface
deve ser implementada:
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 ' ,
' / '
)
);
}
}
}
Você pode acessar as relações existentes carregadas de Anotações, XML ou YAML com $classMetadata->getRelations()
.
Se $classMetadata
tiver relações, ou se você adicionar relações a ele, suas relações serão armazenadas em cache. Portanto, se você ler arquivos de configuração (anotações, XML ou YAML), certifique-se de referenciá-los nos metadados da classe:
$ 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 >
Consulte o arquivo hateoas.xsd
para obter mais detalhes.
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())" ]
Esta anotação pode ser definida em uma classe.
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: ' ... ' ,
)]
Propriedade | Obrigatório | Contente | Linguagem de expressão |
---|---|---|---|
nome | Sim | corda | Não |
href | Se incorporado não estiver definido | string /@Route | Sim |
integrado | Se href não estiver definido | string /@Embedded | Sim |
atributos | Não | variedade | Sim em valores |
exclusão | Não | @Exclusão | N / D |
Importante: attributes
são usados apenas em relações de link (ou seja, combinados com a propriedade href
, não com a propriedade 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 ' ,
),
)]
Esta anotação pode ser definida na propriedade href da anotação @Relation. Isso permite que você acesse seu gerador de URL, se você tiver configurado um.
Propriedade | Obrigatório | Contente | Linguagem de expressão |
---|---|---|---|
nome | Sim | corda | Não |
parâmetros | O padrão é array() | matriz / string | Sim (string + valores de array) |
absoluto | O padrão é falso | booleano / string | Sim |
gerador | Não | string/nulo | Não |
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 ' ,
),
)]
Esta anotação pode ser definida na propriedade incorporada da anotação @Relation. É útil se você precisar configurar as opções exclusion
ou xmlElementName
para o recurso incorporado.
Propriedade | Obrigatório | Contente | Linguagem de expressão |
---|---|---|---|
contente | Sim | string/matriz | Sim (sequência) |
exclusão | O padrão é array() | @Exclusão | N / D |
xmlElementName | O padrão é array() | corda | Não |
Esta anotação pode ser definida na propriedade de exclusão das anotações @Relation e @Embedded.
Propriedade | Obrigatório | Contente | Linguagem de expressão |
---|---|---|---|
grupos | Não | variedade | Não |
sinceVersion | Não | corda | Não |
atéversão | Não | corda | Não |
profundidade máxima | Não | inteiro | Não |
excluirSe | Não | string / booleano | Sim |
Todos os valores, exceto excludeIf
agem da mesma maneira que quando são usados diretamente nas propriedades regulares com o serializador.
excludeIf
espera um booleano e é útil quando outra expressão falha em algumas circunstâncias. Neste exemplo, se o método getManager
for null
, você deverá excluí-lo para evitar falha na geração da 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 {}
}
Esta anotação pode ser definida em uma classe. É útil se você deseja serializar relações múltiplas (links). Por exemplo:
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}
Propriedade | Obrigatório | Contente | Linguagem de expressão |
---|---|---|---|
nome | Sim | corda | Sim |
Pode ser "nome":
my_func
MyClass::getExtraRelations
expr(service('user.rel_provider').getExtraRelations())
Aqui está um exemplo usando a linguagem de expressão:
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
{
...
}
Aqui está a classe 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
implementando CompilableExpressionEvaluatorInterface
é usado para analisar a linguagem de expressão em um formato que pode ser armazenado em cache e salvo para uso posterior. Se você não precisa da linguagem de expressão em suas relações, então este serviço não é necessário.
O serviço user.rel_provider
é definido como:
user.rel_provider :
class : UserRelPrvider
arguments :
- ' @jms_serializer.expression_evaluator '
Neste caso, jms_serializer.expression_evaluator
é um serviço que implementa CompilableExpressionEvaluatorInterface
.
Esta seção refere-se aos internos do Hateoas, fornecendo documentação sobre partes ocultas desta biblioteca. Isso nem sempre é relevante para os usuários finais, mas é interessante para desenvolvedores ou pessoas interessadas em aprender como as coisas funcionam nos bastidores.
willdurand/hateoas
segue o versionamento semântico.
A partir de outubro de 2013, as versões 1.x
0.x
oficialmente não são mais suportadas (observe que 1.x
nunca foi lançado).
A versão 3.x
é a versão estável principal atual.
A versão 2.x
é mantida apenas para correções de bugs de segurança e para problemas importantes que possam ocorrer.
Consulte arquivo CONTRIBUINDO.
Instale as dependências dev
do Composer:
php composer.phar install --dev
Em seguida, execute o conjunto de testes usando PHPUnit:
bin/phpunit
Hateoas é lançado sob a licença MIT. Consulte o arquivo LICENSE incluído para obter detalhes.