Links and Resources
The basic building blocks of this component are links and resources:
Mezzio\Hal\Link
Mezzio\Hal\HalResource
Note on naming
Why HalResource
and not the simpler Resource
?
The answer: PHP.
As of PHP 7, resource
has been designated a potential future language keyword.
In order to be forwards compatible, we opted to name our class HalResource
.
PSR-13
mezzio/mezzio-hal implements PSR-13, which provides interfaces for relational links and collections of relational links.
Mezzio\Hal\Link
implements Psr\Link\EvolvableLinkInterface
, and Mezzio\Hal\HalResource
implements Psr\Link\EvolvableLinkProviderInterface
.
Resources compose links, so we'll cover links first.
Links
Links provide URIs to related resources.
Any given link, therefore, needs to compose the relation and a URI. Additionally, links:
- can be templated. Templated links have one or more
{variable}
placeholders in them that clients can then fill in in order to generate a fully qualified URI. - can contain a number of other attributes: type, title, name, etc.
To model these, we provide the Mezzio\Hal\Link
class. It has the
following constructor:
public function __construct(
$relation,
string $uri = '',
bool $isTemplated = false,
array $attributes = []
)
$relation
can be a string value, or an array of string values, representing
the relation.
To access these various properties, you can use the following methods:
$link->getRels() // get the list of relations for the link
$link->getHref() // retrieve the URI
$link->isTemplated() // is the link templated?
$link->getAttributes() // get any additional link attributes
A Link
is immutable; you cannot change it after the fact. If you need a
modified version of the link, we provide several methods that will return new
instances containing the changes:
$link = $link->withRel($rel); // or provide an array of relations
$link = $link->withoutRel($rel); // or provide an array of relations
$link = $link->withHref($href);
$link = $link->withAttribute($attribute, $value);
$link = $link->withoutAttribute($attribute);
With these tools, you can describe any relational link.
Route-based link URIs
Most frameworks provide routing capabilities, and delegate URI generation to
their routers to ensure that generated links conform to known routing
specifications. Link
expects only a string URI, however; how can you prevent
hard-coding that URI?
This component provides a tool for that: Mezzio\Hal\LinkGenerator
.
This class composes a Mezzio\Hal\LinkGenerator\UrlGeneratorInterface
instance, which defines the following:
namespace Mezzio\Hal\LinkGenerator;
use Psr\Http\Message\ServerRequestInterface;
interface UrlGeneratorInterface
{
/**
* Generate a URL for use as the HREF of a link.
*
* - The request is provided, to allow the implementation to pull any
* request-specific items it may need (e.g., results of routing, original
* URI for purposes of generating a fully-qualified URI, etc.).
*
* - `$routeParams` are any replacements to make in the route string.
*
* - `$queryParams` are any query string parameters to include in the
* generated URL.
*/
public function generate(
ServerRequestInterface $request,
string $routeName,
array $routeParams = [],
array $queryParams = []
) : string;
}
We provide a default implementation for Mezzio users,
Mezzio\Hal\LinkGenerator\MezzioUrlGenerator
, that uses the
UrlHelper
and ServerUrlHelper
from mezzio-helpers in order to
generate URIs.
The LinkGenerator
itself defines two methods:
$link = $linkGenerator->fromRoute(
$relation,
$request,
$routeName,
$routeParams, // Array of additional route parameters to inject
$queryParams, // Array of query string arguments to inject
$attributes // Array of Link attributes to use
);
$link = $linkGenerator->templatedFromRoute(
$relation,
$request,
$routeName,
$routeParams, // Array of additional route parameters to inject
$queryParams, // Array of query string arguments to inject
$attributes // Array of Link attributes to use
);
fromRoute()
will generate a non-templated Link
instance, while
templatedFromRoute()
generates a templated instance.
If you need to generate custom links based on routing, we recommend composing
the LinkGenerator
in your own classes to do so.
Limitation
There is a known limitation with laminas-router when using routes with optional parameters (e.g.,
/books[/:id]
, where:id
is optional). In such cases, if no matching parameter is provided (such as when generating a URI without an:id
), the router will raise an exception due to the missing parameter.If you encounter this issue, create separate routing entries for each optional parameter. See the issue for a comprehensive example.
Resources
A HAL resource is simply the representation you want to return for your API.
Mezzio\Hal\HalResource
allows you to model these representations,
along with any relational links and child resources.
It defines the following constructor:
public function __construct(
array $data = [],
array $links = [],
array $embedded = []
)
$data
should be an associative array of data you wish to include in your
representation; the only limitation is you may not use the keys _links
or
_embedded
, as these are reserved keywords.
$links
should be an array of Mezzio\Hal\Link
instances.
$embedded
should be an array of Mezzio\Hal\HalResource
instances.
Most often, however, you will include these with $data
, as the class contains
logic for identifying them.
Once you have created an instance, you can access its properties:
$resource->getElement($name) // retrieve an element or embedded resource by name
$resource->getElements() // retrieve all elements and embedded resources
$resource->getLinks() // retrieve all relational links
$resource->getLinksByRel() // retrieve links for a specific relation
$resource->toArray() // retrieve associative array representation
HalResource
instances are immutable. We provide a number of methods that
allow you to create new instances with changes:
$resource = $resource->withElement($name, $value);
$resource = $resource->withoutElement($name);
$resource = $resource->withElements($elements);
$resource = $resource->embed($name, $resource);
$resource = $resource->withLink($link);
$resource = $resource->withoutLink($link);
Embed Empty Collection
New Feature
Available since version 2.7.0.
To maintain consistency in the structure of the response, you may choose to embed both non-empty and empty collections within the _embedded
section. This can be achieved by enabling the embed-empty-collections
configuration option.
To enable this feature, modify the configuration file config/autoload/hal.global.php
as follows:
return [
'mezzio-hal' => [
'embed-empty-collections' => false, // (default: false for compatibility reasons)
'metadata-factories' => [...],
'resource-generator' => [...],
],
];
The default setting of false
ensures compatibility with existing API endpoints and prevents potential test failures.
When embed-empty-collections
is set to false
, the representation will be as follows:
{
"contacts": []
}
However, when embed-empty-collections
is set to true
, the representation will be as follows:
{
"_embedded": {
"contacts": []
}
}
With these tools, you can describe any resource you want to represent.