Child ResourcesΒΆ

Resources often do not exist in isolation. Besides some resources embedding others, in some cases, a resource exists only as a result of another resource existing – in other words, within a hierarchical or tree structure. Such resources are often given the name “child resources.”

In the advanced routing chapter, we looked at one such example, with a user and addresses.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
'users' => array(
    'type' => 'Segment',
    'options' => array(
        'route' => '/users[/:user_id]',
        'controller' => 'UserResourceController',
    ),
    'may_terminate' => true,
    'child_routes' => array(
        'addresses' => array(
            'type' => 'Segment',
            'options' => array(
                'route' => '/addresses[/:address_id]',
                'controller' => 'UserAddressResourceController',
            ),
        ),
    ),
),

In that chapter, I looked at how to tie into various events in order to alter routing parameters, which would ensure that the relational URLs were generated correctly. I also noted that there’s a better approach: metadata maps. Let’s look at such a solution now.

First, let’s make some assumptions:

  • Users are of type User, and can be hydrated using the ClassMethods hydrator.
  • Individual addresses are of type UserAddress, and can by hydrated using the ObjectProperty hydrator.
  • Address collections have their own type, UserAddresses.
  • The users collection is called “users”
  • The address collection is called “addresses”
  • The class UserListener listens on Resource events for users.
  • The class UserAddressListener listens on Resource events for addresses.

Now, let’s create some resource controllers, using configuration as noted in the chapter on resource controllers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
return array(
    // ...
    'phlyrestfully' => array(
        'resources' => array(
            'UserResourceController' => array(
                'listener'                => 'UserListener',
                'collection_name'         => 'users',
                'collection_http_options' => array('get', 'post'),
                'resource_http_options'   => array('get', 'patch', 'put', 'delete'),
                'page_size'               => 30,
            ),
            'UserAddressResourceController' => array(
                'listener'                => 'UserAddressListener',
                'collection_name'         => 'addresses',
                'collection_http_options' => array('get', 'post'),
                'resource_http_options'   => array('get', 'patch', 'put', 'delete'),
            ),
        ),
    ),
);

Now we have controllers that can respond properly. Let’s now configure the metadata and hydrator maps for our resources.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
return array(
    // ...
    'phlyrestfully' => array(
        // ...
        'metadata_map' => array(
            'User' => array(
                'hydrator'        => 'ClassMethods',
                'identifier_name' => 'user_id',
                'route'           => 'users',
            ),
            'UserAddress' => array(
                'hydrator'        => 'ObjectProperty',
                'identifier_name' => 'address_id',
                'route'           => 'users/addresses',
            ),
            'UserAddresses' => array(
                'identifier_name' => 'address_id',
                'route'           => 'users/addresses',
                'is_collection'   => true,
                'route_options'   => array('query' => true),
            ),
        ),
    ),
);

Now, when we render a User, if it composes a UserAddresses object, that object will be rendered as an embedded collection, and each resource inside it will be rendered using the appropriate route and identifier.

Project Versions

Previous topic

Alternate resource return values

Next topic

Classes Available

This Page