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 response - it's got a Content-Type
of text/html
. I thought
we fixed that! Well, that's no surprise - when we switched from JsonResponse
to Response
, we lost that header. But more importantly, this mistake is
too easy to make: we're calling serialize() and then creating the Response
by hand in every controller. That means we'd need to set this header everywhere.
That sucks. Let's centralize this across our entire project.
First, move serialize()
out of ProgrammerController
and into a class
called BaseController
. This is something I created and all controllers
extend this. Paste this at the bottom and make it protected
:
... lines 1 - 15 | |
abstract class BaseController extends Controller | |
{ | |
... lines 18 - 122 | |
protected function serialize($data, $format = 'json') | |
{ | |
return $this->container->get('jms_serializer') | |
->serialize($data, $format); | |
} | |
} |
And while we're here - make another function: protected function createApiResponse()
.
Give it two arguments: $data
and $statusCode
that defaults to 200:
... lines 1 - 15 | |
abstract class BaseController extends Controller | |
{ | |
... lines 18 - 113 | |
protected function createApiResponse($data, $statusCode = 200) | |
{ | |
... lines 116 - 120 | |
} | |
... lines 122 - 127 | |
} |
Instead of creating the Response
ourselves, we can just call this and it'll
take care of the details. Inside, first serialize the $data
- whatever
that is. And then return a new Response()
with that $json
, that $statusCode
and - most importantly - that Content-Type
header of application/json
so we don't forget to set that:
... lines 1 - 113 | |
protected function createApiResponse($data, $statusCode = 200) | |
{ | |
$json = $this->serialize($data); | |
return new Response($json, $statusCode, array( | |
'Content-Type' => 'application/json' | |
)); | |
} | |
... lines 122 - 129 |
I love it! Let's use this everywhere! Search for new Response
. Call
$response = $this->createApiResponse()
and pass the $programmer
. Copy
that line and make sure it's status code is 201. Remove the other stuff,
but keep the line that sets the Location
header:
... lines 1 - 15 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 18 - 21 | |
public function newAction(Request $request) | |
{ | |
... lines 24 - 33 | |
$response = $this->createApiResponse($programmer, 201); | |
... lines 35 - 38 | |
$response->headers->set('Location', $programmerUrl); | |
return $response; | |
} | |
... lines 43 - 138 | |
} |
Ok, much easier. Find the rest of the new Response
spots and update them.
It's all pretty much the same - listAction()
has a different variable name,
but that's it. For deleteAction()
, well, it's returning a null
Response,
so we can leave that one alone.
... lines 1 - 47 | |
public function showAction($nickname) | |
{ | |
... lines 50 - 60 | |
$response = $this->createApiResponse($programmer, 200); | |
return $response; | |
} | |
... lines 65 - 69 | |
public function listAction() | |
{ | |
... lines 72 - 75 | |
$response = $this->createApiResponse(['programmers' => $programmers], 200); | |
return $response; | |
} | |
... lines 80 - 84 | |
public function updateAction($nickname, Request $request) | |
{ | |
... lines 87 - 104 | |
$response = $this->createApiResponse($programmer, 200); | |
return $response; | |
} | |
... lines 109 - 140 |
Let's re-run the tests!
phpunit -c app
They still fail, but the responses have the right Content-Type
header.
Time to fix these failures, and see how we can control the serializer.
Hey Shaun,
It would have more sense if you want to unit-test this serializer service or if you need to reuse it in another spot, like in a service, etc. But for now, this serialize() method contains a single line of code, so there's no code duplication. It probably makes sense to create an abstract controller and move that method to it if you need to use that serialize() method in a few controllers.
Cheers!
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "2.6.*", // v2.6.11
"doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
"doctrine/dbal": "<2.5", // v2.4.4
"doctrine/doctrine-bundle": "~1.2", // v1.4.0
"twig/extensions": "~1.0", // v1.2.0
"symfony/assetic-bundle": "~2.3", // v2.6.1
"symfony/swiftmailer-bundle": "~2.3", // v2.3.8
"symfony/monolog-bundle": "~2.4", // v2.7.1
"sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
"sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"hautelook/alice-bundle": "0.2.*", // 0.2
"jms/serializer-bundle": "0.13.*" // 0.13.0
},
"require-dev": {
"sensio/generator-bundle": "~2.3", // v2.5.3
"behat/behat": "~3.0", // v3.0.15
"behat/mink-extension": "~2.0.1", // v2.0.1
"behat/mink-goutte-driver": "~1.1.0", // v1.1.0
"behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
"phpunit/phpunit": "~4.6.0" // 4.6.4
}
}
Would it be best practice to have the serializer method in a service class rather than in the base controller?