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:

All metadata types inherit from Zend\Expressive\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:

We aggregate metadata in a Zend\Expressive\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 Zend\Expressive\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 Zend\Expressive\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 Zend\Expressive\Hal\Metadata\MetadataMap;
use Zend\Expressive\Hal\Metadata\RouteBasedCollectionMetadata;
use Zend\Expressive\Hal\Metadata\RouteBasedResourceMetadata;
use Zend\Hydrator\ObjectProperty;

return [
    'Zend\Expressive\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:

ResourceGenerator

Once you have defined the metadata for the various objects you will represent in your API, you can start generating resources.

Zend\Expressive\Hal\ResourceGenerator has the following constructor:

public function __construct(
    Zend\Expressive\Hal\Metadata\MetadataMap $metadataMap,
    Psr\Container\ContainerInterface $hydrators,
    Zend\Expressive\Hal\LinkGenerator $linkGenerator
) {

We described the MetadataMap in the previous section, and the LinkGenerator in the previous chapter.

Hydrators are defind in the zend-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 Zend\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.)