If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Hey, we have a working endpoint to view a single programmer! We’re awesome :) Now do you remember the Location response header we return after creating a new programmer? Let’s update that to be a real value.
To do this, first add a bind function to our programmer route:
// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...
protected function addRoutes(ControllerCollection $controllers)
{
$controllers->post('/api/programmers', array($this, 'newAction'));
$controllers->get('/api/programmers/{nickname}', array($this, 'showAction'))
->bind('api_programmers_show');
}
This gives the route an internal name of api_programmers_show. We can use that below to generate a proper URL to the new programmer resource:
// src/KnpU/CodeBattle/Controller/Api/ProgrammerController.php
// ...
public function newAction(Request $request)
{
// ...
$response = new Response('It worked. Believe me - I\'m an API', 201);
$programmerUrl = $this->generateUrl(
'api_programmers_show',
['nickname' => $programmer->nickname]
);
$response->headers->set('Location', $programmerUrl);
return $response;
}
The generateUrl method is a shortcut I added for our app, and it combines the nickname with the rest of the URL. You may make URLs differently in your app, but the idea is the same: set the Location header to the URI where I can GET this new resource.
Tip
The generateUrl method is just a shortcut for doing this:
$programmerUrl = $this->container['url_generator']->generate(
'api_programmers_show',
['nickname' => $programmer->nickname]
);
Update the testing.php script to print out the response from the original POST so we can check this out:
// testing.php
// ...
// 1) Create a programmer resource
$request = $client->post('/api/programmers', null, json_encode($data));
$response = $request->send();
echo $response;
echo "\n\n";
die;
// 2) GET a programmer resource
// ...
And when we run it again, we’ve got a working Location header:
HTTP/1.1 201 Created
...
Location: /api/programmers/ObjectOrienter330
It worked. Believe me - I'm an API
The Location header is more than just a nice thing. Its purpose is to help the client know where to go next without needing to hardcode URLs or URL patterns. To prove this, we can update our testing script to read the Location header and use it for the next request. This lets us remove the hardcoded URL pattern we had before:
// testing.php
// ...
// 1) Create a programmer resource
$request = $client->post('/api/programmers', null, json_encode($data));
$response = $request->send();
$programmerUrl = $response->getHeader('Location');
// 2) GET a programmer resource
$request = $client->get($programmerUrl);
$response = $request->send();
echo $response;
echo "\n\n";
If the URL pattern to view a programmer changes in the future, our client won’t break. That’s really powerful. But it’s also where things start to get complicated. More on that later, dear warrior.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"silex/silex": "~1.0", // v1.3.2
"symfony/twig-bridge": "~2.1", // v2.7.3
"symfony/security": "~2.4", // v2.7.3
"doctrine/dbal": "^2.5.4", // v2.5.4
"monolog/monolog": "~1.7.0", // 1.7.0
"symfony/validator": "~2.4", // v2.7.3
"symfony/expression-language": "~2.4" // v2.7.3
},
"require-dev": {
"behat/mink": "~1.5", // v1.5.0
"behat/mink-goutte-driver": "~1.0.9", // v1.0.9
"behat/mink-selenium2-driver": "~1.1.1", // v1.1.1
"behat/behat": "~2.5", // v2.5.5
"behat/mink-extension": "~1.2.0", // v1.2.0
"phpunit/phpunit": "~5.7.0", // 5.7.27
"guzzle/guzzle": "~3.7" // v3.9.3
}
}