How to render a string as a Twig template in Symfony2

Sometimes you might want to store a twig template in something other than a file, for instance in a database or in a variable. Here’s a nice and easy solution to this problem.

Update: I have switched my preferred method over to using Twig’s Twig_Loader_Chain class. This method allows you to assign multiple loaders to one instance of a Twig_Environment so you can load templates from files and strings seamlessly. You can see this method at the bottom of this article. Thanks to Matthias Noback for contacting me about this.

In order to render a Twig template you need an instance of the Twig_Environment class. This class contains the render method that returns parsed output from a Twig template (usually from a file).

Twig Environments use loader classes that use the interface Twig_LoaderInterface to load data to be parsed into a template. Symfony2 uses the Twig_Loader_Filesystem class by default that loads data from template files, in order to render a template from a string we need an instance of Twig_Environment that uses the Twig_Loader_String class to obtain it’s data.

There are a few ways you can do this, the easiest is to simply create an instance of Twig_Environment and tell it to use Twig_Loader_String during instantiation (this code can be placed inside your controller):

$twig = new \Twig_Environment(new \Twig_Loader_String());
$rendered = $twig->render(
  "Test string template: {{ result }}",
  array("result" => "Success!")
);

This works fine for a quick fix but the problem here is that within the template you will not have access to any Symfony extensions or custom extensions that you have created, this is because we have created a fresh Twig_Environment instance without any of the extra configuration that Symfony provides.

A better approach is to take a copy of the existing Twig_Environment service used by Symfony and alter it to use Twig_Loader_String (you can use this within your controller):

$twig = clone $this->get('twig');
$twig->setLoader(new \Twig_Loader_String());
$rendered = $twig->render(
  "Test string template: {{ result|humanize }}",
  array("result" => "mega_success")
);

Here I have cloned the original Symfony service to make sure that Symfony can still render a template from a file as per usual.

Another method is to create your own Twig service by using Symfony’s dependy injection system. Here I have added a custom Twig service into my projects config.yml:

services:
    mine.twig_string_loader:
        class:        "Twig_Loader_String"
    mine.twig_string:
        class:        "%twig.class%"
        arguments:    [@mine.twig_string_loader, %twig.options% ]

You can then use this service like so within your controller:

$rendered = $this->get('mine.twig_string')->render(
   "Test string template: {{ result }}",
   array("result" => "Success!")
);

Unfortunately this method has the same pitfalls as the first method, it will not include any Symfony Twig extensions or your own custom extensions. Of course you could add these extensions into this custom Twig instance, but it is far easier to simply clone Symfony’s existing Twig service and alter that as needed.

Using Twig’s Chain Loader

As mentioned above, the preferred method is to use Twig’s chain loader to assign multiple loaders to Symfony’s main Twig instance. To use this method you must be using Symfony2.2 or above.

Adding an extra loader to Symfony’s Twig instance can be achieved by creating a new string loader definition like so:

twig.stringloader:
    class: Twig_Loader_String
    tags:
        - { name: twig.loader }

This can be placed within your projects config.yml file or a bundle specific service config file.

The important bit here is the tag ‘twig.loader’ this tells Symfony to assign the class Twig_Loader_String as an extra loader to the main Twig instance.

The beauty of this method is that Symfony will render file and string templates seamlessly without any extra effort, you can simply call the render method with either a template file reference or a raw string and Twig will figure out which loader to use for you.

Here’s an example of this in action:

class DefaultController extends Controller
{
    public function indexAction()
    {
      $renderedString = $this->get('twig')->render('{{ name }}', array('name' => 'test'));
      $renderedTemplate = $this->get('twig')->render('MyBundle:Default:index.html.twig');
    }
}

Both calls to render will successfully render Twig output, the Twig service will figure out which loader to use based on the input provided.