Embedding ResourcesΒΆ

At times, you may want to embed resources inside other resources. As an example, consider a “user” resource: it may need to embed several addresses, multiple phone numbers, etc.:

HAL dictates the structure for the representation:

{
    "_links": {
        "self": {
            "href": "http://example.org/api/user/mwop"
        }
    },
    "id": "mwop",
    "full_name": "Matthew Weier O\'Phinney",
    "_embedded": {
        "url": {
            "_links": {
                "self": "http://example.org/api/user/mwop/url/mwop_net"
            },
            "url_id": "mwop_net",
            "url": "http://www.mwop.net/"
        },
        "phones": [
            {
                "_links": {
                    "self": "http://example.org/api/user/mwop/phones/1"
                },
                "phone_id": "mwop_1",
                "type": "mobile",
                "number": "800-555-1212"
            }
        ]
    }
}

However, what if our objects look like this:

 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
26
27
28
class User
{
    public $id;
    public $full_name;

    /**
     * @var Url
     \*/
    public $url;

    /**
     * @var Phone[]
     \*/
    public $phones;
}

class Url
{
    public $url_id;
    public $url;
}

class Phone
{
    public $phone_id;
    public $type;
    public $number;
}

How, exactly, do we ensure that the $url and $phones properties are rendered as embedded resources?

The explicit way to handle it is within your listeners: assign the value of these properties to a HalResource or HalCollection (depending on whether they are single resources or collections of resources, respectively).

1
2
3
$user = $persistence->fetch($id);
$user->addresses = new HalResource($user->url, $user->url->url_id);
$user->phones    = new HalCollection($user->phones, 'api/user/phone');

From here, you can use the techniques covered in the advanced routing, advanced rendering, and hydrators sections to ensure that the various relational links are rendered correctly, and that the resources are properly rendered.

This is fairly straight-forward, but ultimately inflexible and prone to error. Many times, the properties will not be public, and in many circumstances, the setters will require specific, typed objects. As such, making a change like this will not work.

You can work around it by creating either a proxy resource object, or converting the resource to an array. However, there’s a better way: metadata maps.