Usage & Configuration
Usage & Configuration
Configuring Links
Links are relations that are identified by a name (e.g. self) and
that carry an href parameter, which is the actual URI pointing to
the target resource. You configure links on a class by adding
@Hateoas\Relation annotations to it.
The following example shows the most basic configuration for a User class,
adding a self link that points to the user resource:
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() {}
}
The library supports several configuration formats: annotations, XML, and YAML. Annotations are recommended as they keep the configuration close to the code itself.
Now, use the HateoasBuilder to create a serializer that is capable of
serializing HATEOAS representations:
use Hateoas\HateoasBuilder;
$hateoas = HateoasBuilder::create()->build();
$user = new User(42, 'Adrien', 'Brault');
$json = $hateoas->serialize($user, 'json');
$xml = $hateoas->serialize($user, 'xml');
The $json variable will contain the following
HAL compliant representation:
{
"id": 42,
"first_name": "Adrien",
"last_name": "Brault",
"_links": {
"self": {
"href": "/api/users/42"
}
}
}
The $xml variable will contain the following
Atom Links representation:
<user id="42">
<first_name><![CDATA[Adrien]]></first_name>
<last_name><![CDATA[Brault]]></last_name>
<link rel="self" href="/api/users/42"/>
</user>
Embedding Resources
Sometimes it is helpful to embed related resources rather than linking to them.
The @Hateoas\Relation annotation supports an embedded
parameter that allows you to inline a related resource directly within your
representation.
The following example adds a manager relation that embeds the
manager user object alongside its link:
use JMS\Serializer\Annotation as Serializer;
use Hateoas\Configuration\Annotation as Hateoas;
/**
* @Serializer\XmlRoot("user")
*
* @Hateoas\Relation("self", href = "expr('/api/users/' ~ object.getId())")
* @Hateoas\Relation(
* "manager",
* href = "expr('/api/users/' ~ object.getManager().getId())",
* embedded = "expr(object.getManager())"
* )
*/
class User
{
/** @Serializer\XmlAttribute */
private $id;
private $firstName;
private $lastName;
/** @Serializer\Exclude */
private $manager;
}
The JSON representation will include both links and embedded resources in HAL format:
{
"id": 42,
"first_name": "Adrien",
"last_name": "Brault",
"_links": {
"self": {
"href": "/api/users/42"
},
"manager": {
"href": "/api/users/1"
}
},
"_embedded": {
"manager": {
"id": 1,
"first_name": "William",
"last_name": "Durand",
"_links": {
"self": {
"href": "/api/users/1"
}
}
}
}
}
The XML representation will inline the embedded resource:
<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/1"/>
<user rel="manager" id="1">
<first_name><![CDATA[William]]></first_name>
<last_name><![CDATA[Durand]]></last_name>
<link rel="self" href="/api/users/1"/>
</user>
</user>
Note: The
managerproperty is excluded from serialization using@Serializer\Excludebecause it is already exposed through the embedded relation. The embedded XML element name is determined by the @XmlRoot annotation on the target class.
Dealing With Collections
Hateoas provides three representation classes for dealing with collections of
resources: CollectionRepresentation,
PaginatedRepresentation, and
OffsetRepresentation. These classes add pagination metadata and
navigation links to your collections automatically.
The following example creates a paginated collection of users:
use Hateoas\Representation\PaginatedRepresentation;
use Hateoas\Representation\CollectionRepresentation;
$paginatedCollection = new PaginatedRepresentation(
new CollectionRepresentation($pager->getCurrentPageResults()),
'user_list', // route
array(), // route parameters
$pager->getCurrentPage(), // page number
$pager->getMaxPerPage(), // limit
$pager->getNbPages(), // total pages
'page', // page route parameter name, optional
'limit', // limit route parameter name, optional
true, // generate absolute URIs, optional
$pager->getNbResults() // total collection size, optional
);
The JSON representation will contain the pagination metadata and navigation links:
{
"page": 1,
"limit": 10,
"pages": 10,
"total": 100,
"_links": {
"self": {
"href": "/api/users?page=1&limit=10"
},
"first": {
"href": "/api/users?page=1&limit=10"
},
"last": {
"href": "/api/users?page=10&limit=10"
},
"next": {
"href": "/api/users?page=2&limit=10"
}
},
"_embedded": {
"items": [
{ "id": 1 },
{ "id": 2 },
{ "id": 3 }
]
}
}
The XML representation:
<collection page="1" limit="10" pages="10" total="100">
<user id="1"/>
<user id="2"/>
<user id="3"/>
<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=10&limit=10"/>
<link rel="next" href="/api/users?page=2&limit=10"/>
</collection>
If you are using Pagerfanta,
you can use the PagerfantaFactory to create paginated representations
more easily:
use Hateoas\Representation\Factory\PagerfantaFactory;
$pagerfantaFactory = new PagerfantaFactory();
$paginatedCollection = $pagerfantaFactory->createRepresentation(
$pager,
new Route('user_list', array())
);
You can customize the CollectionRepresentation and the XML root
element name by passing additional arguments:
$paginatedCollection = $pagerfantaFactory->createRepresentation(
$pager,
new Route('user_list', array()),
new CollectionRepresentation(
$pager->getCurrentPageResults(),
'users', // embedded rel, optional
'users' // XML element name, optional
)
);
Representations
VndErrorRepresentation
The VndErrorRepresentation allows you to describe error responses
following the vnd.error
specification. This is useful for returning standardized error responses from
your API.
use Hateoas\Representation\VndErrorRepresentation;
$error = new VndErrorRepresentation(
'Validation Failed',
42,
'The request could not be processed due to validation errors.',
'http://example.com/help',
'http://example.com/log/42'
);
The XML representation:
<resource logref="42">
<message><![CDATA[Validation Failed]]></message>
<link rel="help" href="http://example.com/help"/>
<link rel="describes" href="http://example.com/log/42"/>
</resource>
The JSON representation:
{
"message": "Validation Failed",
"logref": 42,
"_links": {
"help": {
"href": "http://example.com/help"
},
"describes": {
"href": "http://example.com/log/42"
}
}
}