Buy

Google for "HATEOAS PHP" to find a fun library that a friend of mine made. This library has a bundle that integrates it into Symfony: so click to view the BazingaHateoasBundle and go straight to its docs. Before we talk about what it does: get it installed.

Installing BazingaHateoasBundle

Copy the composer require statement and then flip over to your terminal and paste that:

composer require willdurand/hateoas-bundle

This is a bundle, so grab the new bundle statement, open AppKernel and pop that at the bottom:

54 lines app/AppKernel.php
... lines 1 - 5
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
... lines 11 - 21
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
);
... lines 24 - 33
}
... lines 35 - 52
}

Perfect. Currently, we have our own super sweet annotation system for adding links. In Battle, we use @Link to create a programmer link:

138 lines src/AppBundle/Entity/Battle.php
... lines 1 - 9
/**
... lines 11 - 13
* @Link(
* "programmer",
* route="api_programmers_show",
* params={"nickname": "object.getProgrammerNickname()"}
* )
*/
class Battle
... lines 21 - 138

Guess what! I completely stole that idea from this library. But now, to make our app a little simpler and to get some new features, let's replace our @Link code with this library.

Go back to the library itself, and scroll down to the first coding example. This uses an annotation system that looks pretty similar to ours. Copy the use statement on top, open Battle and paste that:

141 lines src/AppBundle/Entity/Battle.php
... lines 1 - 8
use Hateoas\Configuration\Annotation as Hateoas;
... lines 10 - 141

Next, change the annotation to @Hateoas\Relation:

141 lines src/AppBundle/Entity/Battle.php
... lines 1 - 10
/**
... lines 12 - 14
* @Hateoas\Relation(
... lines 16 - 20
* )
*/
class Battle
... lines 24 - 141

Keep programmer: that will still be the link's rel. But add href=@Hateoas\Route and pass that the name of the route: api_programmers_show:

141 lines src/AppBundle/Entity/Battle.php
... lines 1 - 10
/**
... lines 12 - 14
* @Hateoas\Relation(
* "programmer",
* href=@Hateoas\Route(
* "api_programmers_show",
... line 19
* )
* )
*/
class Battle
... lines 24 - 141

Update params to parameters, and inside, set nickname equal, and wrap the expression in expr():

141 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())"}
* )
* )
*/
class Battle
... lines 24 - 141

That translates our annotation format to the one used by the bundle. And the result is almost the same. Open BattleControllerTest and copy the first method name:

76 lines tests/AppBundle/Controller/Api/BattleControllerTest.php
... lines 1 - 6
class BattleControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTCreateBattle()
... lines 17 - 74
}

we have a test for a link near the bottom of this:

76 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',
$this->adjustUri('/api/programmers/Fred')
);
... lines 46 - 48
}
... lines 50 - 74
}

Change over to the terminal and, as long as Composer is done, run:

vendor/bin/phpunit --filter testPOSTCreateBattle

Check it out! It fails - but barely. This library still puts links under an _links key, but instead of listing the URLs directly, it wraps each inside an object with an href key. That's causes the failure.

Ok, fair enough. Let's fix that by updating the test to look for _links.programmer.href:

76 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')
);
... lines 46 - 48
}
... lines 50 - 74
}

Run the test again:

vendor/bin/phpunit --filter testPOSTCreateBattle

And now we're green.

Holy Toledo Batman: This is HAL JSON!

But guess what? It's no accident that this library used this exact format: with an _links key and an href below that. This is a semi-official standard format called HAL JSON.

Leave a comment!