This course is archived!
This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ?valid, but the code we use can't be used in a real application.
HATEOAS Loves Routers
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.
This is great, except that I don't like this hardcoded URL in the Relation
:
// ... lines 1 - 7 | |
use Hateoas\Configuration\Annotation as Hateoas; | |
// ... line 9 | |
/** | |
* @Serializer\ExclusionPolicy("all") | |
* @Hateoas\Relation("self", href = "expr('/api/programmers/' ~ object.nickname)") | |
*/ | |
class Programmer | |
{ | |
// ... lines 16 - 52 | |
} |
What I'd rather do is generate that URL from the internal name api_programmers_show
like we normally do.
Fortunately, the HATEOAS library allows us to do that,
and this can be hooked up to work with any framework, since they all generate
URLs. As part of that HateoasBuilder::create()
step, you can set a URL generator
that does whatever you need.
Hooking up the SymfonyUrlGenerator
If you're using Silex or Symfony, life is a little bit easier because there's
a built-in class called SymfonyUrlGenerator
:
// from the HATEOAS docs
use Hateoas\UrlGenerator\SymfonyUrlGenerator;
$hateoas = HateoasBuilder::create()
->setUrlGenerator(null, new SymfonyUrlGenerator($app['url_generator']))
->build()
;
I'll copy this line. Go back into Application
, move build()
onto the
next line and paste this:
// ... lines 1 - 6 | |
use Hateoas\UrlGenerator\SymfonyUrlGenerator; | |
// ... lines 8 - 150 | |
private function configureServices() | |
{ | |
$app = $this; | |
// ... lines 154 - 217 | |
$this['serializer'] = $this->share(function() use ($app) { | |
// ... lines 219 - 224 | |
// create the Hateoas serializer | |
return HateoasBuilder::create($jmsBuilder) | |
->setUrlGenerator(null, new SymfonyUrlGenerator($app['url_generator'])) | |
->build(); | |
}); | |
// ... lines 230 - 233 | |
} | |
// ... lines 235 - 352 |
And don't forget that we need a use
statement for that SymfonyUrlGenerator
,
so I'll click "import" to have PhpStorm add this class to the top of the
file for me. This class comes from the HATEOAS library, and we're just passing
it the url_generator
object, which in Silex, is the object responsible
for generating URLs. In Symfony, it's called router
.
Using the Router in Annotations
With this, we can go back into Programmer
. First, I'm going to move things
onto multiple lines for my sanity. Instead of setting the href
to a URL,
we'll say @HATEOAS\Route
. I'll make sure I have all my parenthesis in
the right place. In the Route, we'll have 2 arguments - the first is the
name of the route. The second argument is whatever variables we need to pass
into the route, in a parameters
key. Because the route has the {nickname}
,
we're going to pass that here using the expression language again. This time,
we'll say object
- because that represents the Programmer - .nickname
.
That's fancy way of saying: "generate me a URL, and here's the nickname to
use in that URL.":
// ... lines 1 - 9 | |
/** | |
* @Serializer\ExclusionPolicy("all") | |
* @Hateoas\Relation( | |
* "self", | |
* href = @Hateoas\Route( | |
* "api_programmers_show" | |
* parameters = { "nickname" = expr("object.nickname") } | |
* ) | |
* ) | |
*/ | |
class Programmer | |
// ... lines 21 - 60 |
Unless I've messed something up, the test should pass like before:
php vendor/bin/behat features/api/programmer.feature:66
Ah, and
it doesn't! I messed up some syntax. Anytime you see the Doctrine\Common\Annotations
T_CLOSE_PARENTHESIS
type of thing, this is a syntax error in your annotation.
I'm missing a comma between my arguments. Let's try that one more time.
Ah, I messed up again! If you look back at the docs, which I've been ignoring,
you can see that the quotes should be around the entire nickname
value.
I'll fix that, and learn that it's always good to follow the docs:
// ... lines 1 - 9 | |
/** | |
* @Serializer\ExclusionPolicy("all") | |
* @Hateoas\Relation( | |
* "self", | |
* href = @Hateoas\Route( | |
* "api_programmers_show", | |
* parameters = { "nickname" = "expr(object.nickname)" } | |
* ) | |
* ) | |
*/ | |
// ... lines 20 - 60 |
And this time it finally passes. So other than my syntax error, that was easy to fix up. And if this looks overwhelming to you, that's ok. From now on, we're just going to be copying and pasting this and customizing it for whatever links we need.