Una biblioteca PHP para admitir la implementación de representaciones para servicios web REST de HATEOAS.
La forma recomendada de instalar Hateoas es a través de Composer. Solicite el paquete willdurand/hateoas
ejecutando el siguiente comando:
composer require willdurand/hateoas
Esto resolverá la última versión estable.
De lo contrario, instale la biblioteca y configure el cargador automático usted mismo.
Si desea utilizar anotaciones para la configuración, debe instalar el paquete doctrine/annotations
:
composer require doctrine/annotations
Si su aplicación usa PHP 8.1 o superior, se recomienda utilizar atributos PHP nativos. En este caso no es necesario instalar el paquete Doctrine.
¡Hay un paquete para eso! ¡Instala BazingaHateoasBundle y disfruta!
Importante:
Para aquellos que usan la versión
1.0
, pueden ir a esta página de documentación.Para aquellos que usan la versión
2.0
, pueden ir a esta página de documentación.La siguiente documentación ha sido escrita para Hateoas 3.0 y superiores.
Hateoas aprovecha la biblioteca Serializer para proporcionar una buena manera de crear servicios web HATEOAS REST. HATEOAS significa Hipermedia como motor del estado de la aplicación y agrega enlaces de hipermedia a sus representaciones (es decir, sus respuestas API). HATEOAS trata sobre la capacidad de descubrimiento de acciones en un recurso.
Por ejemplo, digamos que tiene una API de usuario que devuelve una representación de un único usuario de la siguiente manera:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe "
}
}
Para decirles a sus consumidores de API cómo recuperar los datos de este usuario específico, debe agregar su primer enlace a esta representación, llamémosla self
ya que es el URI de este usuario en particular:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe " ,
"_links" : {
"self" : { "href" : " http://example.com/api/users/123 " }
}
}
}
Profundicemos ahora en Hateoas.
En la terminología de Hateoa, los enlaces se consideran relaciones añadidas a los recursos. Vale la pena mencionar que las relaciones también se refieren a recursos integrados , pero este tema se tratará en la sección Recursos integrados.
Un enlace es una relación que se identifica por un name
(por ejemplo, self
) y que tiene un 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 () {}
}
En el ejemplo anterior, configuramos una relación self
que es un enlace debido al parámetro href
. Su valor, que puede parecer extraño a primera vista, se tratará ampliamente en la sección El lenguaje de expresión. Este valor especial se utiliza para generar un URI.
En esta sección, las anotaciones/atributos se utilizan para configurar Hateoas. También se admiten los formatos XML y YAML . Si lo deseas, también puedes utilizar PHP simple.
Importante: debes configurar tanto el Serializer como el Hateoas de la misma manera. Por ejemplo, si usa YAML para configurar Serializer, use YAML para configurar Hateoas.
La forma más sencilla de probar HATEOAS es con HateoasBuilder
. El constructor tiene numerosos métodos para configurar el serializador Hateoas, pero no profundizaremos en ellos ahora (consulte The HateoasBuilder). Todo funciona bien desde el primer momento:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()-> build ();
$ user = new User ( 42 , ' Adrien ' , ' Brault ' );
$ json = $ hateoas -> serialize ( $ user , ' json ' );
$ xml = $ hateoas -> serialize ( $ user , ' xml ' );
El objeto $hateoas
es una instancia de JMSSerializerSerializerInterface
, procedente de la biblioteca Serializer. Hateoas no viene con su propio serializador, se conecta al serializador JMS.
De forma predeterminada, Hateoas utiliza el lenguaje de aplicación de hipertexto (HAL) para la serialización JSON. Esto especifica la estructura de la respuesta (por ejemplo, que los "enlaces" deben estar bajo una clave _links
):
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
Para XML, los enlaces Atom se utilizan de forma predeterminada:
< user id = " 42 " >
< first_name > <![CDATA[ Adrien ]]> </ first_name >
< last_name > <![CDATA[ Brault ]]> </ last_name >
< link rel = " self " href = " /api/users/42 " />
</ user >
Cabe mencionar que estos formatos son los predeterminados , no los únicos disponibles. Puede utilizar diferentes formatos a través de diferentes serializadores e incluso agregar los suyos propios.
Ahora que sabe cómo agregar enlaces , veamos cómo agregar recursos integrados .
A veces, es más eficiente incorporar recursos relacionados en lugar de vincularlos, ya que evita que los clientes tengan que realizar solicitudes adicionales para recuperar esos recursos.
Un recurso incrustado es una relación con nombre que contiene datos, representados por el 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: Deberá excluir la propiedad del administrador de la serialización; de lo contrario, tanto el serializador como Hateoas la serializarán. También tendrá que excluir la relación del administrador cuando el administrador sea null
, porque de lo contrario se producirá un error al crear el enlace href
(llamando getId()
en null
).
Consejo: si la propiedad del administrador es un objeto que ya tiene un enlace _self
, puede reutilizar ese valor para href
en lugar de repetirlo aquí. Consulte 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 ' );
Para json
, la representación HAL coloca estas relaciones incrustadas dentro de una clave _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 "
}
}
}
}
}
En XML, la serialización de relaciones embedded
creará nuevos 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 >
El nombre de la etiqueta de un recurso incrustado se infiere de la anotación @XmlRoot
( xml_root_name
en YAML, xml-root-name
en XML) procedente de la configuración del serializador.
La biblioteca proporciona varias clases en el espacio de nombres HateoasRepresentation*
para ayudarle con tareas comunes. Estas son clases simples configuradas con las anotaciones de la biblioteca.
Las clases PaginatedRepresentation
, OffsetRepresentation
y CollectionRepresentation
son probablemente las más interesantes. Estos son útiles cuando su recurso es en realidad una colección de recursos (por ejemplo, /users
es una colección de usuarios). Estos le ayudan a representar la colección y agregar paginación y límites:
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
ofrece una representación básica de una colección integrada.
PaginatedRepresentation
está diseñada para agregar enlaces self
, first
y, cuando sea posible, last
, next
y previous
.
OffsetRepresentation
funciona igual que PaginatedRepresentation
pero es útil cuando la paginación se expresa mediante offset
, limit
y total
.
RouteAwareRepresentation
agrega una relación self
basada en una ruta determinada.
Puede generar URI absolutos configurando el parámetro absolute
en true
tanto en PaginatedRepresentation
como en RouteAwareRepresentation
.
La biblioteca Hateoas también proporciona PagerfantaFactory
para crear fácilmente PaginatedRepresentation
desde una instancia de Pagerfanta. Si utiliza la biblioteca Pagerfanta, esta es una forma más sencilla de crear las representaciones de la colección:
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 ' );
Obtendría el siguiente contenido 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 }
]
}
}
Y el siguiente contenido 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 >
Si desea personalizar la CollectionRepresentation
en línea, pase uno como tercer argumento del 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 ' );
Si desea cambiar el nombre raíz xml de la colección, cree una nueva clase con la raíz xml configurada y use el mecanismo en línea:
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 );
Como se mencionó en la sección anterior, las representaciones son clases configuradas con las anotaciones de la biblioteca para ayudarlo con tareas comunes. Las representaciones de cobro se describen en Cómo tratar con el cobro.
VndErrorRepresentation
le permite describir una respuesta de error siguiendo la especificación vnd.error
.
$ error = new VndErrorRepresentation (
' Validation failed ' ,
42 ,
' http://.../ ' ,
' http://.../ '
);
Serializar dicha representación en XML y JSON le daría los siguientes 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://.../ "
}
}
}
Sugerencia: se recomienda crear sus propias clases de error que extiendan la clase VndErrorRepresentation
.
Hateoas se basa en el potente componente Symfony ExpressionLanguage para recuperar valores como enlaces, identificadores u objetos para incrustar.
Cada vez que completa un valor (por ejemplo, una relación href
en anotaciones o YAML), puede pasar un valor codificado o una expresión . Para utilizar el lenguaje de expresión, debe utilizar la notación 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()) " )]
Puede obtener más información sobre la sintaxis de expresión leyendo la documentación oficial: La sintaxis de expresión.
De forma nativa, una variable especial denominada object
está disponible en cada expresión y representa el objeto actual:
expr(object.getId())
A esta variable la llamamos variable de contexto .
Puede agregar sus propias variables de contexto al contexto del lenguaje de expresión agregándolas al evaluador de expresiones.
Usando HateoasBuilder
, llame al método setExpressionContextVariable()
para agregar nuevas variables de contexto:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setExpressionContextVariable ( ' foo ' , new Foo ())
-> build ();
La variable foo
ya está disponible:
expr(foo !== null)
Para obtener más información sobre cómo agregar funciones al lenguaje de expresión, consulte https://symfony.com/doc/current/components/expression_language/extending.html
Dado que puede utilizar el lenguaje de expresión para definir los enlaces de relaciones (tecla href
), puede hacer muchas cosas de forma predeterminada. Sin embargo, si está utilizando un marco, es probable que desee utilizar rutas para crear enlaces.
Primero deberá configurar un UrlGenerator
en el constructor. Puede implementar HateoasUrlGeneratorUrlGeneratorInterface
o utilizar 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 ()
;
Luego podrá utilizar la anotación @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 "
}
}
}
Tenga en cuenta que la biblioteca viene con SymfonyUrlGenerator
. Por ejemplo, para usarlo en Silex:
use Hateoas UrlGenerator SymfonyUrlGenerator ;
$ hateoas = HateoasBuilder:: create ()
-> setUrlGenerator ( null , new SymfonyUrlGenerator ( $ app [ ' url_generator ' ]))
-> build ()
;
Hateoas proporciona un conjunto de ayudas para facilitar el proceso de creación de API.
La clase LinkHelper
proporciona un método getLinkHref($object, $rel, $absolute = false)
que le permite obtener el valor href de cualquier objeto, para cualquier nombre de relación determinado. Es capaz de generar un URI (ya sea absoluto o relativo) a partir de cualquier relación de enlace :
$ 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
La característica anterior también está disponible en sus expresiones (cf. The Expression Language) a través de la función 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 atención a las expresiones href
para la post
y las relaciones relative
, así como sus valores correspondientes en el siguiente contenido 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 la pena mencionar que puedes forzar si quieres un URI absoluto o relativo usando el tercer argumento tanto en el método getLinkHref()
como en la función link
.
Importante: por defecto todas las URI serán relativas , incluso aquellas que estén definidas como absolutas en su configuración.
$ 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 también proporciona un conjunto de extensiones Twig.
LinkExtension
le permite utilizar LinkHelper en sus plantillas Twig, para que pueda generar enlaces en sus plantillas HTML, por ejemplo.
Esta extensión expone el método auxiliar getLinkHref()
a través de la función 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 proporciona un conjunto de serializadores . Cada serializador le permite generar contenido XML o JSON siguiendo un formato específico, como HAL o Atom Links, por ejemplo.
JsonHalSerializer
le permite generar relaciones compatibles con HAL en JSON. Es el serializador JSON predeterminado en Hateoas.
HAL proporciona su capacidad de vinculación con una convención que dice que un objeto de recurso tiene una propiedad reservada llamada _links
. Esta propiedad es un objeto que contiene enlaces. Estos enlaces están codificados por su relación de enlace.
HAL también describe otra convención que dice que un recurso puede tener otra propiedad reservada llamada _embedded
. Esta propiedad es similar a _links
en que los recursos integrados tienen una clave por nombre de relación. La principal diferencia es que, en lugar de ser enlaces, los valores son objetos de recursos.
{
"message" : " Hello, World! " ,
"_links" : {
"self" : {
"href" : " /notes/0 "
}
},
"_embedded" : {
"associated_events" : [
{
"name" : " SymfonyCon " ,
"date" : " 2013-12-12T00:00:00+0100 "
}
]
}
}
XmlSerializer
le permite generar enlaces Atom en sus documentos XML. Es el serializador XML predeterminado.
<? 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
le permite generar relaciones compatibles con HAL en XML.
HAL en XML es similar a HAL en JSON en el sentido de que describe etiquetas link
y etiquetas resource
.
Nota: la self
en realidad se convertirá en un atributo del recurso principal en lugar de ser una etiqueta link
. Otros enlaces se generarán como etiquetas 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 >
Debe implementar SerializerInterface
que describe dos métodos para serializar enlaces y relaciones incrustadas .
La clase HateoasBuilder
se utiliza para configurar Hateoas fácilmente gracias a una API potente y fluida.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setCacheDir ( ' /path/to/cache/dir ' )
-> setDebug ( $ trueOrFalse )
-> setDefaultXmlSerializer ()
. . .
-> build ();
Todos los métodos siguientes devuelven el constructor actual, para que puedas encadenarlos.
setXmlSerializer(SerializerInterface $xmlSerializer)
: establece el serializador XML que se utilizará. El valor predeterminado es: XmlSerializer
;setDefaultXmlSerializer()
: establece el serializador XML predeterminado ( XmlSerializer
). setJsonSerializer(SerializerInterface $jsonSerializer)
: establece el serializador JSON que se utilizará. El valor predeterminado es: JsonHalSerializer
;setDefaultJsonSerializer()
: establece el serializador JSON predeterminado ( JsonHalSerializer
). setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
: agrega un nuevo generador de URL con nombre. Si $name
es null
, el generador de URL será el predeterminado. setExpressionContextVariable($name, $value)
: agrega una nueva variable de contexto de expresión;setExpressionLanguage(ExpressionLanguage $expressionLanguage)
; includeInterfaceMetadata($include)
: si se incluyen los metadatos de las interfaces;setMetadataDirs(array $namespacePrefixToDirMap)
: establece un mapa de prefijos de espacios de nombres para directorios. Este método anula cualquier directorio definido previamente;addMetadataDir($dir, $namespacePrefix = '')
: agrega un directorio donde el serializador buscará metadatos de clase;addMetadataDirs(array $namespacePrefixToDirMap)
: agrega un mapa de prefijos de espacios de nombres a los directorios;replaceMetadataDir($dir, $namespacePrefix = '')
: similar a addMetadataDir()
, pero anula una entrada existente.Lea la documentación oficial del Serializer para obtener más detalles.
setDebug($debug)
: habilita o deshabilita el modo de depuración;setCacheDir($dir)
: establece el directorio de caché.Tanto el serializador como las bibliotecas Hateoas recopilan metadatos sobre sus objetos de diversas fuentes, como YML, XML o anotaciones. Para que este proceso sea lo más eficiente posible, se recomienda permitir que la biblioteca Hateoas almacene en caché esta información. Para hacer eso, configure un directorio de caché:
$ builder = Hateoas HateoasBuilder:: create ();
$ hateoas = $ builder
-> setCacheDir ( $ someWritableDir )
-> build ();
Hateoas admite varias fuentes de metadatos. De forma predeterminada, utiliza anotaciones de Doctrine (PHP < 8.1) o atributos PHP nativos (PHP >= 8.1), pero también puedes almacenar metadatos en archivos XML o YAML. Para esto último, es necesario configurar un directorio de metadatos donde se ubican esos archivos:
$ hateoas = Hateoas HateoasBuilder:: create ()
-> addMetadataDir ( $ someDir )
-> build ();
Hateoas esperaría que los archivos de metadatos tuvieran nombres como los nombres de clase completos donde todos se reemplazan por
.
. Si su clase se llamaría VendorPackageFoo
el archivo de metadatos debería estar ubicado en $someDir/Vendor.Package.Foo.(xml|yml)
.
Hateoas permite que los marcos agreguen dinámicamente relaciones a las clases proporcionando un punto de extensión en el nivel de configuración. Esta característica puede ser útil para aquellos que desean crear una nueva capa encima de Hateoas o agregar relaciones "globales" en lugar de copiar la misma configuración en cada clase.
Para aprovechar este mecanismo, se debe implementar la interfaz 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 ' ,
' / '
)
);
}
}
}
Puede acceder a las relaciones existentes cargadas desde Anotaciones, XML o YAML con $classMetadata->getRelations()
.
Si $classMetadata
tiene relaciones, o si le agrega relaciones, sus relaciones se almacenarán en caché. Entonces, si lees archivos de configuración (Anotaciones, XML o YAML), asegúrate de hacer referencia a ellos en los metadatos de la clase:
$ 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 el archivo hateoas.xsd
para obtener más detalles.
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 anotación se puede definir en una clase.
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: ' ... ' ,
)]
Propiedad | Requerido | Contenido | lenguaje de expresión |
---|---|---|---|
nombre | Sí | cadena | No |
href | Si incrustado no está configurado | cadena / @Ruta | Sí |
incorporado | Si href no está configurado | cadena / @Embedded | Sí |
atributos | No | formación | Si a los valores |
exclusión | No | @Exclusión | N / A |
Importante: attributes
sólo se utilizan en relaciones de enlace (es decir, combinados con la propiedad href
, no con la 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 anotación se puede definir en la propiedad href de la anotación @Relation. Esto le permite acceder a su generador de URL, si ha configurado uno.
Propiedad | Requerido | Contenido | lenguaje de expresión |
---|---|---|---|
nombre | Sí | cadena | No |
parámetros | El valor predeterminado es matriz() | matriz/cadena | Sí (cadena + valores de matriz) |
absoluto | El valor predeterminado es falso | booleano/cadena | Sí |
generador | No | cadena / nulo | No |
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 anotación se puede definir en la propiedad incrustada de la anotación @Relation. Es útil si necesita configurar las opciones exclusion
o xmlElementName
para el recurso integrado.
Propiedad | Requerido | Contenido | lenguaje de expresión |
---|---|---|---|
contenido | Sí | cadena/matriz | Sí (cadena) |
exclusión | El valor predeterminado es matriz() | @Exclusión | N / A |
xmlElementName | El valor predeterminado es matriz() | cadena | No |
Esta anotación se puede definir en la propiedad de exclusión de las anotaciones @Relation y @Embedded.
Propiedad | Requerido | Contenido | lenguaje de expresión |
---|---|---|---|
grupos | No | formación | No |
desdeVersión | No | cadena | No |
hasta la versión | No | cadena | No |
profundidad máxima | No | entero | No |
excluir si | No | cadena / booleano | Sí |
Todos los valores, excepto excludeIf
actúan de la misma manera que cuando se usan directamente en las propiedades normales con el serializador.
excludeIf
espera un valor booleano y es útil cuando otra expresión falla en algunas circunstancias. En este ejemplo, si el método getManager
es null
, debes excluirlo para evitar que falle la generación de la 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 anotación se puede definir en una clase. Es útil si desea serializar múltiples relaciones (enlaces). Como ejemplo:
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}
Propiedad | Requerido | Contenido | lenguaje de expresión |
---|---|---|---|
nombre | Sí | cadena | Sí |
Puede ser "nombre":
my_func
MyClass::getExtraRelations
expr(service('user.rel_provider').getExtraRelations())
Aquí un ejemplo usando el lenguaje de expresión:
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
{
...
}
Aquí la clase 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
que implementa CompilableExpressionEvaluatorInterface
se usa para analizar el lenguaje de expresión en un formulario que se puede almacenar en caché y guardar para su uso posterior. Si no necesita el lenguaje de expresión en sus relaciones, entonces este servicio no es necesario.
El servicio user.rel_provider
se define como:
user.rel_provider :
class : UserRelPrvider
arguments :
- ' @jms_serializer.expression_evaluator '
En este caso, jms_serializer.expression_evaluator
es un servicio que implementa CompilableExpressionEvaluatorInterface
.
Esta sección se refiere a las partes internas de Hateoas y proporciona documentación sobre partes ocultas de esta biblioteca. Esto no siempre es relevante para los usuarios finales, pero es interesante para los desarrolladores o las personas interesadas en aprender cómo funcionan las cosas internamente.
willdurand/hateoas
sigue el control de versiones semántico.
A partir de octubre de 2013, las versiones 1.x
y 0.x
oficialmente ya no son compatibles (tenga en cuenta que 1.x
nunca se lanzó).
La versión 3.x
es la principal versión estable actual.
La versión 2.x
se mantiene sólo para corregir errores de seguridad y para problemas importantes que puedan ocurrir.
Ver archivo CONTRIBUCIÓN.
Instale las dependencias dev
de Composer:
php composer.phar install --dev
Luego, ejecute el conjunto de pruebas usando PHPUnit:
bin/phpunit
Hateoas se publica bajo la licencia MIT. Consulte el archivo de LICENCIA incluido para obtener más detalles.