If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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.
To do that, after href
, add an embedded="expr()"
with object.getProgrammer()
:
... 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)
:
... 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:
... 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
:
... 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.
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!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.0.*", // v3.0.3
"doctrine/orm": "^2.5", // v2.5.4
"doctrine/doctrine-bundle": "^1.6", // 1.6.2
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // v2.10.0
"sensio/distribution-bundle": "^5.0", // v5.0.4
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.14
"incenteev/composer-parameter-handler": "~2.0", // v2.1.2
"jms/serializer-bundle": "^1.1.0", // 1.1.0
"white-october/pagerfanta-bundle": "^1.0", // v1.0.5
"lexik/jwt-authentication-bundle": "^1.4", // v1.4.3
"willdurand/hateoas-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.6
"symfony/phpunit-bridge": "^3.0", // v3.0.3
"behat/behat": "~3.1@dev", // dev-master
"behat/mink-extension": "~2.2.0", // v2.2
"behat/mink-goutte-driver": "~1.2.0", // v1.2.1
"behat/mink-selenium2-driver": "~1.3.0", // v1.3.1
"phpunit/phpunit": "~4.6.0", // 4.6.10
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}
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.