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 theClassMethods
hydrator. - Individual addresses are of type
UserAddress
, and can by hydrated using theObjectProperty
hydrator. - Address collections have their own type,
UserAddresses
. - The users collection is called “users”
- The address collection is called “addresses”
- The class
UserListener
listens onResource
events for users. - The class
UserAddressListener
listens onResource
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.