I see several ways to handle this.
Adding a global variable from a kernel.request listener
The idea is to add a global variable straight 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);
}
}
You can now use it at any time by doing
{{ myvar }}
From a kernel.view listener
First, you need to understand when kernel.view is called. It's only called when the return of the controller is not an instance of Response object.
That said, doing
// Acme/FooBundle/FooController#fooAction
return $this->render();
returns a Response object, so kernel.view is not called.
Defining controllers
The idea is to make all controller returns an array of data, just like @Template requirements.
// Acme/FooBundle/FooController#fooAction
return array(
'template' => 'AcmeFooBundle:Foo:foo.html.twig',
'data' => array(
'entity' => $entity
)
);
Defining the service
Since you already have your service definition, you just need to add some requirements in your service declaration.
You need the @templating service to render the data.
You need to set itself 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
Creating the service
// 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 there you are. The listener is creating a new response, adding your custom definition of myvar to any template rendered by him.
From a TWIG extension
An alternative is to create a TWIG extension. In the following example, I'm assuming the MyListener definition is the same as above.
Defining services
As per the documentation given above, you just have 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 }
Defining the service
Just like in documentation, we'll 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();
}
}
Usage
Then you can use it in any view by doing
{{ myvar() }}
From a common controller
I don't like this idea, but this is an alternative. You just have to create a BaseController which 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);
}
}