ramblings on PHP, SQL, the web, politics, ultimate frisbee and what else is on in my life
back

Lets talk about JSON in Symfony2

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:


<?php
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() are done inside the controller making it impossible to change the behavior inside the view layer without a lot of hackery. I think it would make sense to remove this method from the controller and instead move this call to the view layer so that it can be changed without having to touch the controller code.

Agavi takes a cleaner albeit in many cases a bit overboard approach: Here the view layer isn't essentially just loading a bunch of templates, but instead the view layer is a separate service implemented in PHP that sits between the controller and the templates. I would prefer Symfony2 to not require this, but maybe it would make sense to have this optional? Maybe the event system could be used here in order to hook into just before the controller loads a template and just when the template returns its content. Then again the redirect() problem would still require that the calls would be moved out of the controller.

I encourage everyone in the Symfony community to take the time today to think about this issue and provide feedback. Fabien is concerned that we might be making the common use case too complex to solve a niche use case, but I think especially for 3rd party Bundles being able to offer direct HTML next to a JSON output is pretty much a must have these days.

My team's goal here at Liip is to make the DoctrineUserBundle a role model Bundle, which is why we have also invested time to convert the template from php to twig, the controllers and routes to services and a few other cleanups. So we would also appreciate feedback on that Bundle.

Oh and in unrelated but still very important news: I need someone with jQuery skills to help me implement the search UI redesign for ResolutionFinder build using symfony 1.4. If necessary I can also pay a bit out of my own pocket. Just want to have this done.

Comments



Re: Lets talk about JSON in Symfony2

Some quick notes:

First, the Form problem is fixed now

Then, as I've already written on the mailing-list, you can have a View layer like you have one in Agavi. This has supported from day 1 in Symfony2 and has always been supported in symfony1 (that was mandatory in Mojavi and made optional in symfony1).

So, in your case, you can provide a View where you do the template stuff, the redirect, and so on.

Re: Lets talk about JSON in Symfony2

Great to here the form issue is solved!

As for the View situation. Like I said in the post, we just disagree if this is a niche feature or a must have. In my opinion the above use case is a must have which needs to be supported with as little code as possible, definitely with no code in the controller. You seem to disagree saying that its enough that its possible, but Bundle authors need to manually make this work.

Re: Lets talk about JSON in Symfony2

yeah, probably a decision consistent with the general approach of the new framework version to let the admin generator be separate. then again its one of the key features of symfony 1.x and probably also a good test to the viability of the new form system. so while maybe the admin generator doesnt need to be finished for the final release, it should definitely be in alpha stage by that time imho. but of course first people need to step up. not sure also yet how important the admin generator will be for our symfony CMF efforts. as for ESI. i would already be quite happy if there would be a simple example in the sandbox (or is there one and i just overlooked it)

essay

very interesting article! I will follow your themes.
Can I subscribe to your posts on Twitter or on your Facebook profile?