How to send var to view from event listener in symfony2?

I am trying to send var to be viewed from an event listener in symfony2, but I'm complex.

1) Is it possible?

2) Which kernel should be used (kernel.view or kernel.request)?

3) Finally, how to send multiple var to view?

My best guess is that I should extend the return from the controller and then let the controller do its work.

I need to send some array of objects (objects).

+7
php symfony
source share
3 answers

I see several ways to handle this.

Adding a global variable from kernel.request

The idea is to add a global variable right after the kernel.request event.

services.yml

 services: class: Acme\FooBundle\Listener\MyListener arguments: - @twig tags: - name: kernel.event_listener event: kernel.request method: onKernelRequest 

Mylistener

 class MyListener { protected $twig; public function __construct(\Twig_Environment $twig) { $this->twig = $twig; } public function onKernelRequest(GetResponseEvent $event) { $myVar = 'foo'; // Process data $this->twig->addGlobal('myvar', $myVar); } } 

Now you can use it anytime by doing

 {{ myvar }} 

From the kernel.view

First, you need to understand when kernel.view is kernel.view . It is called only when the controller return is not an instance of the Response object .
However, doing

 // Acme/FooBundle/FooController#fooAction return $this->render(); 

returns a response object , so kernel.view not called.

Controller Definition

The idea is to force the entire controller to return an array of data, just like @Template requirements.

 // Acme/FooBundle/FooController#fooAction return array( 'template' => 'AcmeFooBundle:Foo:foo.html.twig', 'data' => array( 'entity' => $entity ) ); 

Service definition

Since you already have a service definition, you just need to add some requirements to the service declaration.
To render data, you need the @templating service.
You must set yourself as a kernel.view listener

 // Acme/FooBundle/Resources/config/services.yml services: acme_foo.my_listener: class: Acme\FooBundle\Listener\MyListener arguments: - @templating tags: - name: kernel.event_listener event: kernel.request method: onKernelRequest - name: kernel.event_listener event: kernel.view method: onKernelView 

Service Creation

 // Acme/FooBundle/Listener/MyListener.php use Symfony\Component\Templating\EngineInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; class MyListener { protected $templating; protected $myVar; public function __construct(EngineInterface $templating) { $this->templating = $templating; } public function getMyVar() { return $this->myVar; } public function onKernelRequest(GetResponseEvent $event) { $this->myVar = ""; // Process MyVar data } public function onKernelView(GetResponseForControllerResultEvent $event) { $result = $event->getControllerResult(); if (null === $this->myVar || !isset($result['template']) || !isset($result['data'])) { return; } $data = array_merge($result['data'], array('myvar' => $this->myVar)); $rendered = $this->templating->render($result['template'], $data); $event->setResponse(new Response($rendered)); } } 

And here you are. The listener creates a new response by adding his custom myvar definition to any template he provides.


From the TWIG extension

An alternative is to create a TWIG extension . In the following example, I assume that the definition of MyListener is the same as above.

Service Definition

According to the documentation above, you just need to create a simple extension class.

 // Acme/FooBundle/Resources/config/services.yml services: acme_foo.my_listener: class: Acme\FooBundle\Listener\MyListener tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } acme_foo.my_extension: class: Acme\FooBundle\Extension\MyExtension arguments: - @acme_foo.my_listener tags: - { name: twig.extension } 

Service definition

As in the documentation, we will create a simple function.

 // Acme/FooBundle/Extension/MyExtension.php use Acme\FooBundle\Listener\MyListener; class MyExtension extends \Twig_Extension { protected $listener; public function __construct(MyListener $listener) { $this->listener = $listener; } public function getName() { return 'my_extension'; } public function getFunctions() { return array( 'myvar' => new \Twig_Function_Method($this, 'getMyVar') ); } public function getMyVar() { return $this->listener->getMyVar(); } } 

Using

Then you can use it in any view by doing

 {{ myvar() }} 

From a shared controller

I do not like this idea, but it is an alternative. You just need to create a BaseController that will override the default render method.

 // Acme/FooBundle/Controller/BaseController.php abstract class BaseController extends Controller { public function render($view, array $parameters = array(), Response $response = null) { $parameters = array_merge( $parameters, array( 'myvar' => $this->get('my_listener')->getMyVar() ) ); return parent::render($view, $parameters, $response); } } 
+37
source share

There is an alternative method here that I had to do. I wanted to get some data, run it through json_encode() , and then add JavaScript as a variable in the response. Here is what I did.

I subscribe to kernel.response :

 public static function getSubscribedEvents() { return [ KernelEvents::RESPONSE => 'onKernelResponse' ]; } public function onKernelResponse(FilterResponseEvent $event) { /** -- SNIP -- Cutting out how I get my serialised data **/ $serialized = json_encode($data); /** Shove it into the response as some JS at the bottom **/ $dom = new \DOMDocument; libxml_use_internal_errors(true); $dom->loadHTML($event->getResponse()->getContent()); libxml_use_internal_errors(false); $node = $dom->createElement('script', "var data = $serialized;"); $dom->getElementsByTagName('body')->item(0)->appendChild($node); $event->getResponse()->setContent($dom->saveHTML()); } 

This is one way to do this. Honestly, I do not like the methods described on this page. There must be a better way, but it is not. This is what I use and it works well. Just make sure you don't name the variables "data"; use something that will not be used elsewhere, and it is preferable to stick it in your own namespace (function() { } JS.

+1
source share

I don’t know how to pass variables directly for viewing, but you can change the response object using kernel.response event listener. See how the debug toolbar debug introduces the bottom panel, you can use a similar technique.

https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php#L106

0
source share

All Articles