Metadata Mapping

If you have been reading the reference guide sequentially, almost every page has referred to this one at some point. The reason is that, for purposes of flexibility, PhlyRestfully has needed to provide a low-level, configurable mechanism that solves the problems of:

  • ensuring resources have the correct “self” relational link
  • ensuring resources are extracted to a JSON representation correctly
  • ensuring that embedded resources are rendered as embedded HAL resources

To achieve this in a simpler fashion, PhlyRestfully provides the ability to create a “metadata map.” The metadata map maps a class to a set of “rules” that define whether the class represents a resource or collection, the information necessary to generate a “self” relational link, and a hydrator to use to extract the resource.

This metadata map is defined via configuration. Let’s consider the example from the embedded resources section:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
return array(
    'phlyrestfully' => array(
        'metadata_map' => array(
            'User' => array(
                'hydrator'        => 'ObjectProperty',
                'identifier_name' => 'id',
                'route'           => 'api/user',
            ),
            'Url' => array(
                'hydrator'        => 'ObjectProperty',
                'route'           => 'api/user/url',
                'identifier_name' => 'url_id',
            ),
            'Phones' => array(
                'is_collection'   => true,
                'route'           => 'api/user/phone',
            ),
            'Phone' => array(
                'hydrator'        => 'ObjectProperty',
                'route'           => 'api/user/phone',
                'identifier_name' => 'phone_id',
            ),
        ),
    ),
);

Essentially, the map allows you to associate metadata about how the representation of a resource.

Metadata options

The following options are available for metadata maps:

  • hydrator: the fully qualified class name of a hydrator, or a service name Zend\Hydrator\HydratorPluginManager recognizes, to use to extract the resource. (OPTIONAL)
  • identifier_name: the resource parameter corresponding to the identifier; defaults to “id”. (OPTIONAL)
  • is_collection: boolean flag indicating whether or not the resource is a collection; defaults to “false”. (OPTIONAL)
  • links: array of additional relational links to use with the resource or collection. Each item in the array is itself an array, with the required key “rel” (describing the relation), and one of either “url” (a string) or “route” (an array with the members: “name”, required; “params”, an array, optional; and “options”, an array, optional). (OPTIONAL)
  • resource_route: the name of the route to use for resources embedded as part of a collection. If not set, the route for the resource is used. (OPTIONAL)
  • route: the name of the route to use for generating the “self” relational link. (OPTIONAL; this or url MUST be set, however)
  • route_options: any options to pass to the route when generating the “self” relational link. (OPTIONAL)
  • route_params: any route match parameters to pass to the route when generating the “self” relational link. (OPTIONAL)
  • url: the specific URL to use with this resource. (OPTIONAL; this or route MUST be set, however)

Collections

If you paid careful attention to the example, you’ll note that there is one additional type in the definition, Phones. When creating metadata for a collection, you need to define a first-class type so that HalLinks can match the collection against the metadata map. This is generally regarded as a best practice when doing domain modeling; a type per collection makes it easy to understand what types of objects the collection contains, and allows for domain-specific logic surrounding the collection.

However, that poses some problems if you want to paginate your collection, as instances of Zend\Paginator\Paginator are identified by HalLinks when rendering collections in order to create appropriate relational links.

The solution to that is to create an empty extension of Paginator:

1
2
3
4
5
use Zend\Paginator\Paginator;

class Phones extends Paginator
{
}