On this page
Generating Resources
Generating Resources from PHP Objects
In the previous chapter, we discussed links and resources. The primitive objects allow us to create representations easily, but do not answer one critical question: how can we create resources based on existing PHP object types?
To answer that question, we provide two related features: metadata, and a resource generator.
Metadata
Metadata allows you to detail the requirements for generating a HAL representation of a PHP object. Metadata might include:
- The PHP class name to represent.
- A URI to use for the generated resource's self relational link.
- Alternately, routing information to use with the
LinkGenerator
. - A laminas-hydrator extractor to use to serialize the object to a representation.
- Whether or not the resource is a collection, and, if so, whether pagination is handled as a path parameter or a query string argument, the name of the parameter, etc.
All metadata types inherit from Mezzio\Hal\Metadata\AbstractMetadata
,
which defines a single method, getClass()
, for retrieving the name of the PHP
class to represent; all metadata are expected to inherit from this class.
The component also provides four concrete metadata types, requiring the following information:
Mezzio\Hal\Metadata\RouteBasedCollectionMetadata
:- string
$class
- string
$collectionRelation
- string
$route
- string
$paginationParam = 'page'
(name of the parameter indicating the current page of results) - string
$paginationParamType = self::TYPE_QUERY
(one of "query" or "placeholder") - array
$routeParams = []
(associative array of substitutions to use with the designated route) - array
$queryStringArguments = []
(associative array of query string arguments to include in the generated URI)
- string
Mezzio\Hal\Metadata\RouteBasedResourceMetadata
:- string
$class
- string
$route
- string
$extractor
(string service name of the laminas-hydrator hydrator to use for extracting data from the instance) - string
$resourceIdentifier = 'id'
(name of the property uniquely identifying the resource) - array
$routeParams = []
(associative array of additional routing parameters to substitute when generating the URI) - array
$identifiersToPlacholdersMapping = []
(associative array mapping resource properties to route parameters, for use when generating the URI) - int
$maxDepth = 10
max allowed nesting levels.
- string
Mezzio\Hal\Metadata\UrlBasedCollectionMetadata
:- string
$class
- string
$collectionRelation
- string
$url
- string
$paginationParam = 'page'
(name of the parameter indicating the current page of results) - string
$paginationParamType = self::TYPE_QUERY
(one of "query" or "placeholder")
- string
Mezzio\Hal\Metadata\UrlBasedResourceMetadata
- string
$class
- string
$url
- string
$extractor
(string service name of the laminas-hydrator hydrator to use for extracting data from the instance)
- string
We aggregate metadata in a Mezzio\Hal\Metadata\MetadataMap
instance:
$bookMetadata = new RouteBasedResourceMetadata(
Book::class,
'book',
ObjectPropertyHydrator::class
);
$booksMetadata = new RouteBasedCollectionMetadata(
BookCollection::class,
'book',
'books',
);
$metadataMap = new MetadataMap();
$metadataMap->add($bookMetadata);
$metadataMap->add($booksMetadata);
Configuration-based metadata
To automate generation of the MetadataMap
, we provide
Mezzio\Hal\Metadata\MetadataMapFactory
. This factory may be used with any
PSR-11 container. It utilizes the config
service, and pulls its configuration from a key named after the
Mezzio\Hal\Metadata\MetadataMap
class.
Each item in the map will be an associative array. The member __class__
will
describe which metadata class to create, and the remaining properties will then
be used to generate an instance. As an example, the above could be configured as
follows:
use Api\Books\Book;
use Api\Books\BookCollection;
use Mezzio\Hal\Metadata\MetadataMap;
use Mezzio\Hal\Metadata\RouteBasedCollectionMetadata;
use Mezzio\Hal\Metadata\RouteBasedResourceMetadata;
use Laminas\Hydrator\ObjectProperty;
return [
'Mezzio\Hal\Metadata\MetadataMap' => [
[
'__class__' => RouteBasedResourceMetadata::class,
'resource_class' => Book::class,
'route' => 'book',
'extractor' => ObjectProperty::class,
],
[
'__class__' => RouteBasedCollectionMetadata::class,
'collection_class' => BookCollection::class,
'collection_relation' => 'book',
'route' => 'books',
],
],
];
The rest of the parameters follow underscore delimiter naming convention:
RouteBasedResourceMetadata::class
resource_identifier
(name of the property uniquely identifying the resource)route_params
(associative array of substitutions to use with the designated route)identifiers_to_placeholders_mapping
(associative array mapping resource properties to routing parameters, for use when generating the URI)max_depth
(maximum depth to allow when rendering nested resources; defaults to 10)
RouteBasedCollectionMetadata::class
pagination_param
(name of the parameter indicating the current page of results)pagination_param_type
(one of "query" or "placeholder")route_params
(associative array of substitutions to use with the designated route)query_string_arguments
(associative array of additional routing parameters to substitute when generating the URI)
UrlBasedResourceMetadata::class
resource_class
url
extractor
UrlBasedCollectionMetadata::class
collection_class
collection_relation
url
pagination_param
(name of the parameter indicating the current page of results)pagination_param_type
(one of "query" or "placeholder")
ResourceGenerator
Once you have defined the metadata for the various objects you will represent in your API, you can start generating resources.
Mezzio\Hal\ResourceGenerator
has the following constructor:
public function __construct(
Mezzio\Hal\Metadata\MetadataMap $metadataMap,
Psr\Container\ContainerInterface $hydrators,
Mezzio\Hal\LinkGenerator $linkGenerator
) {
We described the MetadataMap
in the previous section, and the LinkGenerator
in the previous chapter.
Hydrators are defind in the laminas-hydrator component,
and are objects which can hdyrate associative arrays to object instances and
extract associative arrays from object instances. Generally speaking, the
$hydrators
instance may be any PSR-11 container, but you will generally want
to use the Laminas\Hydrator\HydratorPluginManager
.
Once you have your instance created, you can start generating resources:
$resource = $resourceGenerator->fromObject($book, $request);
(Where $request
is a Psr\Http\Message\ServerRequestInterface
instance; the
instance is passed along to the LinkGenerator
in order to generate route-based
URIs for Link
instances.)