I would like to render different views in different context in my Symfony2 project. I'm using multiple routes for the same actions and I would like to render a different page (view) but with the same controller. For example I have:
@Route("/articles/show", name="articles_show")
@Route("/mobile/articles/show", name="mobile_articles_show")
Both routes are using the same action : ArticlesController:showAction(), but should render 2 differents templates (for mobile users and regulars ones).
show.html.twig
mobile.show.html.twig
I do not want to use a if statement or whatever in my controller, so I created a listener (similar to a preExecute function)
Here is a part or my config.yml that defines my listener
services:
controller.pre_execute_listener:
class: MyProject\MyBundle\Listener\ControllerListener
arguments: ["@security.context", "@doctrine", "@router", "@session"]
tags:- { name: kernel.event_listener, event: kernel.controller, method: preExecute }
I was thinking about doing something like that in the listener preExecute function:
if(substr($route,0,7) == 'mobile_'){
$view = 'mobile.'.$view;
}
Unfortunately I cannot find a way to get $view or update the view "on the fly", just before it's rendered.
I hope my question is clear enough, thanks in advance, any idea is welcome :)
J.
Here is the solution:
First I have to listen to kernel.view, not kernel.controller.
Then I use the "@templating" service (Thanks Marko Jovanovic for the hint)
So here is my new config.yml:
services:
controller.pre_execute_listener:
class: MyProject\MyBundle\Listener\ControllerListener
arguments: ["@templating"]
tags:
- { name: kernel.event_listener, event: kernel.view, method: preExecute }
Finally here is my listener preExecute function
public function preExecute(\Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event){
//result returned by the controller
$data = $event->getControllerResult();
/* @var $request \Symfony\Component\HttpFoundation\Request */
$request = $event->getRequest();
$template = $request->get('_template');
$route = $request->get('_route');
if(substr($route,0,7) == 'mobile_'){
$newTemplate = str_replace('html.twig','mobile.html.twig',$template);
//Overwrite original template with the mobile one
$response = $this->templating->renderResponse($newTemplate, $data);
$event->setResponse($response);
}
}
Hope this helps!
J.
Worth noting: The accepted solution doesn't actually work if you directly return a Response
-object (e.g. when you call $this->render()
), because the kernel.view
event is not fired in that case:
If the controller doesn't return a Response object, then the kernel dispatches another event -
kernel.view
.— see Symfony's HTTP Kernel Docs
I couldn't work out a way around this, but found another interesting solution for the same problem: You could simply extend twig's render engine like the ZenstruckMobileBundle does or write your own file locator like the LiipThemeBundle.
[edit:] Alternatively you could also override the TemplateNameParser
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With