Symfony 2 is used for large projects with long term support. These projects usually contain many bundles and the bundles depend on one another. The best option is when bundle A is using features of bundle B, but bundle B is not using features of bundle A (unfortunately, it is not always possible). In this case you can easily outsource some bundles.

Example: you have AcmeBundle and CoreBundle. And in CoreBundle you have main layout. Let us say you have counter on each page and it is located in the layout. To implement this feature you could’ve thought of some of the ways below:

  • Put calculation counter right in the CoreBundle.
  • Create twig block and pass the calculated in AcmeBundle value into template.

In the first approach CoreBundle gets hard dependency from AcmeBundle and the principle of single responsibility is being ruined.

In the second approach you will have to call the same method in each action. You can also create base controller and extend all the applications’ controllers from it, but it does’t fit in the Symfony’s philosophy.

I suggest choosing a different solution, based on using Symfony2 event dispatcher.

Analyzing the Twig and FrameworkBundle source code form the Symfony2 package we can conclude, that we don’t have a pre-render event  before rendering template out of box. That means we should add a pre-render event in our application.

Rendering life cycle in SF2 goes the following way. When you call $this->render(), it passes control to DelegatingEngine — which is an abstract rendering engine. Based on the tamplate name \Symfony\Component\Templating\DelegatingEngine passes control to \Symfony\Bridge\Twig\TwigEngine, \Symfony\Component\Templating\PhpEngine or your own custom template engine (that implements \Symfony\Bundle\FrameworkBundle\Templating\EngineInterface).  

First, you have to decide what events do you need and create class, that describes events.

Then you need to describe event class, that was raised:

Now let’s extend base class DelegatingEngine and add events support:

Finally, config your classes, open services.yml and add the following lines:

That’s all, now create an EventListener in you AcmeBundle and register it in the service container

As a result you have more independent from one another bundles.

11 comments on “Symfony2 and Twig: creating a PRE_RENDER event

  • I like your approach but notice getSubscribedEvents is listening for wrong event name shouldnt be DelegatingEngineEvents::PRE_RENDER since that’s the one you dispatch?

    • Hi Oscar,

      Thanks for noticing typo. Of course, CounterSubscriber should listen for a DelegatingEngineEvents::PRE_RENDER instead of EventableDelegatingEngine::PRE_RENDER.

  • This is exactly the sort of information I’ve been looking for. Thanks! I’ve tried following along, copying and modifying what you have here to my own bundle. At the moment, I have no indication that my pre-render event is being fired off, nor in fact, that my EventableDelegatingEngine is being used. (I’ve stuck XDebug breakpoints all over the place, and nothing’s getting hit.) If you’ve any further pointers, I’d love to hear them.

  • Hi everybody!

    I bring you the solution to dispatch the pre-render event from EventableDelegatingEngine with the Decorate method, just excuse my poor English xD:

    1.- Do the class EventableDelegatingEngine extends TwigEngine, add a private atributte $container, and write the construct method like that:

    public function __construct($container, \Twig_Environment $env, TemplateNameParserInterface $parser, FileLocatorInterface $locator){
    parent::__construct($env, $parser, $locator);
    $this->container = $container;

    2.- Go to your BundleDir\Resources\config\services.yml and add (the name of the service is just like you want):

    class: dir\to\your\EventableDelegatingEngine
    decorates: templating.engine.twig
    arguments: [ "@service_container", "@twig", "@templating.name_parser", "@templating.locator" ]

    And just go to app_dev.php panel > Events and look your event dispached!

    Enjoy!!! :D

Leave a Reply

Your email address will not be published. Required fields are marked *