PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

Lets talk about JSON in Symfony2

Note: This article was originally published at Planet PHP on 28 October 2010.
Planet PHP

So the current project I am working on at Liip is based on Symfony2. The first sprint went surprisingly smooth. The second sprint we are currently in now "fixed" this growing perception that this pre alpha code just might be stable already. Anyway, we knew what we were getting into but still felt it was the best way to go, no regrets. The main source of trouble is the fact that we wanted to make use of DoctrineUserBundle, but load the views via JSON. Unfortunately right now there are quite a few issues with taking a 3rd party Bundle which wasn't specifically build to be accessible via JSON. So in the end I build a MultiplexController that can JSON-ify any controller. The name derives from the fact that it also supports calling multiple routes at once and returning all the data from all these routes in one request.

But being good OSS citizens here at Liip, we decided to use this opportunity to try and come up with a more sustainable solution. The goal would be to make it essentially possible to have a 3rd party controller return JSON without having to do anything on the controller level. In an ideal world all that needs to be done is make some adjustments to the route and to the view layer, because really that should be happening.

Symfony2 already supports a magic solution to make the request aware of the desired output format via the _format pattern label in a route:

foo: pattern: /foo.:_format defaults: { _controller: fooDefault:indexAction, _format: html }

This basically captures the extension used and tells Symfony2 to automatically set the Content-Type according to _format. It also tells the view layer to use different rules for resolving the "logical template name" in the file system.

Lets say "foo.html" is called via the browser and the controller calls return $this-render('FooBundle:Default:index.twig');. This causes Symfony2 to look for a template index.twig. Nothing special there. But lets say "foo.json" is called. In this case Symfony2 will actually look for index.json.twig. In both cases the same parameters are send to the templates. This way its already quite easy to generate different output based on the _format.

Now often when doing JSON, you still want to render html, but via this magic _format handling all included templates will now resolve according to the _format. To make it possible to then still load the index.twig file Fabien just committed a little fix so that calling {% include 'FooBundle:Default:index.html.twig' %}. Note that when resolving the index.html.twig logical template name it actually looks for index.twig in the file system.

So far so good. There are still a few issues here. One is that the above behavior also means that Symfony2 looks for a form.json.twig template when its initializing the twig form extension. Fabien said he will look into that issue.

Also there is no way to call json_encode() inside a twig template for example. Sure one could create a twig token parser for this or a template helper could be registered. But imagine you want to generate a PDF or image file instead, that can get a bit ugly to handle this way.

To make matters "worse" there is no way to extend a twig template with a PHP template otherwise one could simply add {% extends "FooBundle::layout.php" %} to the index.json.twig template in order to wrap the generated output into a JSON structure:

echo json_encode(array('html' = $view['slots']-output('content'), 'status' = 'success',)); ?

Symfony2 also leaves another issue unresolved: When submitting data it has become common practice to redirect afterwards. However this is not the desired nor necessary behavior for JSON requests. But in Symfony2 calls to redirect()

Truncated by Planet PHP, read more at the original (another 2233 bytes)