Buy

Check out the Hal example on their docs. There are actually three different sections of the json: the actual data - like currentlyProcessing, _links and _embedded.

Here's a cool idea: we know that it's nice to add links to a response. These are called relations: they point to related resources. But there's another way to add a relation to a response: embed the related resource right in the JSON.

Remember: the whole point of adding link relations is to make life easier for your API clients. If embedding the data is even easier than advertising a link, do it.

In fact, let's pretend that when we return a Battle resource, we still want to include a link to the related Programmer, but we also want to embed that Programmer entirely.

Adding an Embedded Relation

To do that, after href, add an embedded="expr()" with object.getProgrammer():

142 lines src/AppBundle/Entity/Battle.php
... lines 1 - 10
/**
... lines 12 - 14
* @Hateoas\Relation(
* "programmer",
* href=@Hateoas\Route(
* "api_programmers_show",
* parameters={"nickname"= "expr(object.getProgrammerNickname())"}
* ),
* embedded = "expr(object.getProgrammer())"
* )
*/
class Battle
... lines 25 - 142

Let's see what this looks like! Open BattleControllerTest and right at the bottom, add our handy $this->debugResponse($response):

77 lines tests/AppBundle/Controller/Api/BattleControllerTest.php
... lines 1 - 6
class BattleControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTCreateBattle()
{
... lines 18 - 40
$this->asserter()->assertResponsePropertyEquals(
$response,
'_links.programmer.href',
$this->adjustUri('/api/programmers/Fred')
);
$this->debugResponse($response);
... lines 47 - 49
}
... lines 51 - 75
}

Perfect! Copy that method name and run it:

./vendor/bin/phpunit --filter testPOSTCreateBattle

Oh, cool: we still have the relation in _links, but now we also have an entire programmer resource in _embedded. So when you setup these @Hateoas\Relation annotations:

142 lines src/AppBundle/Entity/Battle.php
... lines 1 - 10
/**
... lines 12 - 14
* @Hateoas\Relation(
* "programmer",
* href=@Hateoas\Route(
* "api_programmers_show",
* parameters={"nickname"= "expr(object.getProgrammerNickname())"}
* ),
* embedded = "expr(object.getProgrammer())"
* )
*/
class Battle
... lines 25 - 142

You can choose whether you want this to be a link or an embedded object.

And OK, we cheated on this test by looking at it first, but now I guess we should specifically have a test for it. Add: $this->asserter()->assertResponsePropertyEquals() with $response. Look for _embedded.programmer.nickname to be equal to our friend Fred:

81 lines tests/AppBundle/Controller/Api/BattleControllerTest.php
... lines 1 - 6
class BattleControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTCreateBattle()
{
... lines 18 - 40
$this->asserter()->assertResponsePropertyEquals(
$response,
'_links.programmer.href',
$this->adjustUri('/api/programmers/Fred')
);
$this->asserter()->assertResponsePropertyEquals(
$response,
'_embedded.programmer.nickname',
'Fred'
);
... lines 51 - 53
}
... lines 55 - 79
}

Run that!

./vendor/bin/phpunit --filter testPOSTCreateBattle

It passes! Now let's customize how these links render.

Leave a comment!

  • 2016-09-08 weaverryan

    Yo Chuck!

    Haha, I don't know :). I mean, the "best practice" is to choose a standard - whether it's HAL, JSON API, or whatever - and put make your responses look like they should. For HAL, you're supposed to put things under _embedded. For JSON API, it's different. So, I would say that *if* you find a standard that you really like and are comfortable following, then follow its rules completely. Otherwise, you're kind of "doing your own thing", which is "frowned upon", but happens all the time. And in that case, I like simple structures, so I just exposed the doctrine relation.

    I hope that helps! There's still a lot in REST that is subjective. I think we *are* moving towards standards everywhere, but those standards (and the tools that make them easy to use) are still developing.

    Cheers!

  • 2016-09-07 Chuck Norris

    Hi,

    I have a small interrogation.

    In a rest world, what is the best between embed the relation with Hatoeos or Expose the doctrine relation ?

    Thanks in advance.