Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Link from Battle to Programmer

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

The Battle representation includes the username of the programmer that fought. If you want more information about Fred, you can make a request to /api/programmers/Fred:

... lines 1 - 6
class BattleControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTCreateBattle()
{
... lines 18 - 37
$this->asserter()
->assertResponsePropertyEquals($response, 'programmer', 'Fred');
... lines 40 - 42
}
... lines 44 - 68
}

That's something we'll document.

But! Wouldn't it be even more convenient if we added a link to that URL inside of the Battle representation? Then, instead of needing to go look up and hardcode the URL, the client could simply read and follow the link.

Whenever a link like this would be helpful, add it! First, look for it in the test: $this->asserter()->assertResponsePropertyEquals(). For consistency, we decided to put links under an _links key. So, look for _links.programmer. This should equal $this->adjustUri('/api/programmers/Fred):

... lines 1 - 37
$this->asserter()
->assertResponsePropertyEquals($response, 'programmer', 'Fred');
$this->asserter()->assertResponsePropertyEquals(
$response,
'_links.programmer',
$this->adjustUri('/api/programmers/Fred')
);
... lines 46 - 76

All this method does is help account for the extra app_test.php that's in the URL when testing:

... lines 1 - 21
class ApiTestCase extends KernelTestCase
{
... lines 24 - 358
/**
* Call this when you want to compare URLs in a test
*
* (since the returned URL's will have /app_test.php in front)
*
* @param string $uri
* @return string
*/
protected function adjustUri($uri)
{
return '/app_test.php'.$uri;
}
}

Perfect! Now, let's go add that link. First, open up the Programmer entity. We added the self link earlier via a cool annotation system we created:

... lines 1 - 9
/**
... lines 11 - 15
* @Link(
* "self",
* route = "api_programmers_show",
* params = { "nickname": "object.getNickname()" }
* )
*/
class Programmer
... lines 23 - 198

In Battle, add something similar: @Link - let that auto-complete for the use statement. Set the name - or rel - of the link to programmer. This is the significance of the link: it could be anything, as long as you consistently use programmer when linking to a programmer:

... lines 1 - 9
/**
... lines 11 - 13
* @Link(
* "programmer",
... lines 16 - 17
* )
*/
class Battle
... lines 21 - 138

For the route, use api_programmers_show: the route name to a single programmer:

... lines 1 - 9
/**
... lines 11 - 13
* @Link(
* "programmer",
* route="api_programmers_show",
... line 17
* )
*/
class Battle
... lines 21 - 138

Finally, add params: the wildcard parameters that need to be passed to the route. This route has a nickname wildcard. Set it to an expression: object.getProgrammerNickname():

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

That's the method we created down below earlier:

... lines 1 - 19
class Battle
{
... lines 22 - 119
/**
* @Serializer\VirtualProperty()
* @Serializer\SerializedName("programmer")
*/
public function getProgrammerNickname()
{
return $this->programmer->getNickname();
}
... lines 128 - 136
}

And that's all we need. Copy the method name again - testPostCreateBattle() - and run the test:

./vendor/bin/phpunit --filter testPostCreateBattle

And it works.

Now, let me show you an awesome library that makes adding links even easier. In fact, I stole the @Link annotation idea from it.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony. The concepts of Hypermedia & HATEOAS are still valid. But I recommend using API Platform in modern Symfony apps.

What PHP libraries does this tutorial use?

// 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
    }
}
userVoice