3 Ways to Reuse Twig Templates in Symfony

In this blog, we will talk about Symfony and how we can reuse Symfony templates. Symfony provides an awesome template engine called Twig. Twig is an open-source framework maintained by Fabien Potencier, and licensed under the BSD license. The first version of this framework was made by Armin Ronacher.

What is Twig?

Twig, which is founded by Symfony, is a modern template engine for PHP. Twig directly compiles the templates into plain PHP code. So the effort of converting into PHP is reduced very much. Like most languages, Twig has a sandbox that protects us from untrusted and suspicious code.  Hence making us use Twig as the template language and it makes Twig more secure. Developers can create their own custom tasks, filters, and DSL too. The output format of Twig is mainly HTML but it can output various text formats as well.

Many open-source projects like Symfony, Drupal 8, eZPublish, PhpBB, Matomo, OroCRM, use Twig as a template engine. Frameworks in which Twig is supported are Slim, Yii, Laravel, and CodeIgniter, etc.

The syntax of Twig is similar to Django and Jinja templates. All the Twig files have extensions of .html.twig and they are a combination of static data such as HTML and Twig Constructs. For output or showing any content on screen Twig uses curly brace delimiter `{{...}}`. For logic, such as conditional statements or loops, it uses curly brace percentage `{%...%}`. It uses curly brace hash `{#...#}` for comments, comments are not rendered on the screen as they are not considered as part of code during compilation time.

Twig is supported in many integrated development environments(IDEs) like Eclipse, Komodo, Komodo edit, Netbeans with Twig syntax plugin, and PhpStorm. Various text editors that are used by developers for Twig, with Twig extension, are Atom, emacs, Notepad++, Sublime Text, Visual Studio Code, Brackets, vim, TextMate, GTK source view, Coda, Coda 2, SubEthaEdit.

An example code for Twig is given below:

{% extends "base.html" %}

{% block sidebar %}
  {{ parent() }}
  <span>Sidebar content specific to this page</span>
{% endblock sidebar %}

{% block body %}
  <p>Select an item:</p>
  <ul>
    {% for item in list %}
      <li><a href="/items/{{ item.id }}">{{ item.name }}</a>
    {% else %}
      <li>No items yet.
    {% endfor %}
  </ul>
{% endblock body %}
Here we have used the `for` tag to create a loop. Other than `for` tags we have used other tags also like `extends`, `block` , etc. Twig code consists of tags, filters, functions, operators, and tests. In Symfony, functions defined by Symfony and Twig filters can also be used and you can even define your own Twig filters and functions.

Basic use

Twig supports two kinds of delimiters: 

{%...%}: It is used for executing conditional and iterative statements.
{{...}}: It is used for printing statements and variables.

Variable: For getting variable in Twig, either for an Object or array,

{{ foo.bar }}
{{ foo[‘bar’] }}

And for setting the value of variables we can do:

{% set foo = ‘foo’ %}
{% set foo = [1,2] %}
{% set foo = {‘foo’: ‘bar’} %}

Filter: Filters like sort can be used like:

{% for number in numbers|sort %}
  {{ number }}
{% endfor %} 

Functions: Functions supported by Twig are used by:

{{ dump(user) }} 

Comment: Comments are used for making code more readable and understandable. In Twig we can use comments as:

{# note: disabled template
   {% for user in users %}
        …
   {% endfor %}
#}

Macros: When we have to reuse a markup HTML to avoid repetition. We define macro by using macro tag.

{% macro input(name, value, type, size) %}

      <input type=”{{ type|default(‘text’) }}” name=”{{ name }}” value=”{{ value|e }}” size=”{{ size|default(20) }}” />

{% endmacro %} 

Using macro:

   {% import “forms.html” as forms %}
    <p>{{ forms.input(‘username’) }}</p>

Setting up Twig

Before going deep into the main features of Twig let us see how you can install Twig via Composer. If your machine does not have a Composer you can always download it from getcomposer.org. Installing the composer is just a matter of some simple clicks when you go to the website. After installing Composer run this command to install Twig:

composer require "twig/twig:^2.0"

Note: Minimum version of PHP required for Twig 2.x is PHP 7.2.5.

For Php 5.x users use the following code:

composer require "twig/twig:~1.0"

Twig works well with both the frontend and backend of any project. So we can look at Twig from two points of view, one as Twig for template designers and the other for Twig as developers. Twig follows the snake case trend for file naming. Snake case means that every space is substituted by `_`. For example, if the file name is blog posts then it should be named as `blog_posts.html.twig`.

Why Use a PHP Templating Engine?

There are many benefits of using the Twig templating engine over basic PHP templates. Twig helps programmers to code in concise, readable, and cleaner codes. We will be seeing how Twig helps us one by one.

  1. Concise: Twig has a very concise syntax. Code logic and readability are not clear enough in the previous PHP templates. You can just check out a WordPress template if you do not believe it. The example code in Twig is given:
{{ var }}
{{ var|escape }}
{{ var|e }} 
  1. Separation: When the project size is bigger, the separation between the UI part and the logic behind it needs to be separated. So the design architecture should separate Model, view, and controllers. However, code by designer and programmer should be merged and here the template comes into the picture. So using a template engine, like Twig, can be a plus point in this case also.

  2. Easy to Learn: Some developers say that PHP is itself a template engine, so why we should not learn Twig. Well, when it comes to code developers want something that is easy to learn and scalable also. Even designers also find it easy to learn and make templates in comparison to very complex languages like PHP.

  3. Caching: Most of the template engine provides a caching feature. It helps to work at a better speed. Twig has better performance with compiled files, which is an extra layer over caching. We have an `Environment` object which is used for loading templates. But if the `cache` option is passed with the corresponding address then Twig will cache the compile and avoid template parsing in subsequent requests. But it is just a cached version for templates. It means the template will be evaluated in all upcoming requests, but Twig has already compiled, parsed, and saved the file for you. 

Code for caching the templates is:

$twig = new Twig_Environment($loader, ['cache' => '/templates/cache']);
  1. Filter, classifiers, and other features: The filter gives us the option to filter the information that is to be sent to the template and what should be seen to the user, in which format. Some of the filters of Twig are, `abs`, `batch`, `capitalize`, `column`, `convert_encoding`, `country_name`, `currency_name`, `data_uri`, `date`, `modify`, etc. There are many other filters, you can find the full list here.
  1. Less chance of broken code: As on one project many developers can work simultaneously, there is a high chance that one developer can disrupt the code of another developer. For solving this problem we have a version control system. 
  1. Debug: Last but not least, debug is the most important part of any application. If we have to access all the information of a template variable, for that purpose Twig has a `dump()` function. We have to add `Twig_Extension_Debug` for the `dump()` function, at the time of creating a Twig environment.
$twig = new Twig_Environment($loader, array('debug' => true));
$twig->addExtension(new Twig_Extension_Debug());
These lines of code are important so we don't release any debug information on a production level. After that, the `dump()` function can be used to dump all template variable information.
{{ dump(products) }}

Reusing Templates in Twig: Three Methods

One of the powerful features of Twig is reusing templates. Reusing is allowed by sharing or extending markup between each template. It is great for those parts which are used globally. For example, the footer and header are common for every page of a website, so it can be extended everywhere. You can use `{% extends %}` to allow markup sharing between templates by extending this template in another template.

Now we are going to discuss three methods of reusing Twig templates.

Including Templates

In a programming language if you have some part of code that is to be used again and again then you can make a function and just call anywhere you want. The same thing happens in Twig. Suppose you have a code snippet that is performing a certain function. If you want functionality again you don’t need to rewrite that code. You can use the `include()` function to use the feature of Twig. 

For example suppose the following code i used to show the profile information of user:

<div class="user-profile">
    <img src="{{ user.profileImageUrl }}" alt="{{ user.fullName }}"/>
    <p>{{ user.fullName }} - {{ user.email }}</p>
</div>
For reusing it first create a new template `blog/_user_profile.html.twig` ( “_” is optional but it is recommended to have a clear distinction between the full template and template fragments.

Then replace the original template with the following template to include a templating fragment.

`{{ include('blog/_user_profile.html.twig') }}`
The only argument that is needed in the `include()` function is the path of the template to be included. Using the with_context parameter you can specify the scope of variables that can be accessed by the included template.
Suppose the variable that contains user information is stored in blog_post.author variable but the included template expects it to be named as a user. In this case, you can pass an optional parameter to rename variables in the included template. Example code for this operation is below:
`{{ include('blog/_user_profile.html.twig', {user: blog_post.author}) }}`
For passing more variables in the included template, you can use the `with` keyword.
{% include 'blog/custom-template' with {'variableName': 'variable value'} %}

{% set vars = {
  myVariable: 'my variable value',
  anotherVar: 'Here is another variable'
} %}
{% include 'snippets/page-header' with vars %}
And for disabling access to the current content you can use the `only` keyword. 
{# No variables will be accessible to the template #}
{% include 'blog/custom-template' only %}
Now suppose you have included a template but after some time or due to some reason you have deleted it. After you have deleted that template then the include statement will throw an error of template not found. But you can suppress the message and ignore the error by using the `ignore missing` keywords. The `ignore missing` is always used after the template name. Other keywords like `only, with` can also be used with it.
{% include 'snippets/header' ignore missing %}
{% include 'snippets/header' ignore missing with {'var': 'value'} %}

Embedding Controllers

Include function allows us to reuse the same code snippet everywhere. But this can not be the best solution when the method has something related to the database query. Suppose you have a template fragment which shows a list of the latest article using a database query. When using the include() function then you have to do the same database queries on every page. 

This method is not efficient and can have a long runtime if there is a large database.

Here the embedding comes into the picture. You can embed the result of some controller that is used for database queries, with the help of render() and controller() Twig functions.

First, you have to write a controller for the desired database query, here fetching the number of articles. 

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class BlogController extends AbstractController
{
    public function recentArticles(int $max = 3): Response
    {
        // get the recent articles somehow (e.g. making a database query)
        $articles = ['...', '...', '...'];

        return $this->render('blog/_recent_articles.html.twig', [
            'articles' => $articles
        ]);
    }
}
Then create a template fragment `blog/_recent_articles.html.twig` (again `_` . This is optional but is recommended to keep template fragment and full template names different. 

{% for article in articles %}
    <a href="{{ path('blog_show', {slug: article.slug}) }}">
        {{ article.title }}
    </a>
{% endfor %}
Now we have created a controller whose output can be used in any template. The example code for using the controller() function is given below. 
<div id="sidebar">
    {# if the controller is associated with a route, use the path() or url() functions #}
    {{ render(path('latest_articles', {max: 3})) }}
    {{ render(url('latest_articles', {max: 3})) }}

    {# if you don't want to expose the controller with a public URL,
       use the controller() function to define the controller to execute #}
    {{ render(controller(
        'App\\Controller\\BlogController::recentArticles', {max: 3}
    )) }}
</div>
In the controller() Twig function, controllers are accessed by special exclusive URLs to serve those template fragments. You can configure that fragment URL option as given below:
framework:
    # ...
    fragments: { path: /_fragment }

Template Inheritance

As the main topic was reusing Symfony templates, the inheritance of templates must be there. In a big website or say a project there are many which are common from page to page. Header, footer, sidebar, social media bar, search bar, etc, many things are almost used on every page. It will be very inefficient and time taking if we have to write code on every page separately. Here the concept of inheritance and embedding controllers can do miracles. But if the pages share a common structure it is advised to use inheritance. 

Twig follows the same inheritance process as PHP does. You have to create a parent template that can be overridden by other child templates for using different parts of the parent template. 

There are three level template inheritance for simple to complex applications:

The example code for base.html.twig template is like this:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}My Application{% endblock %}</title>
        {% block stylesheets %}
            <link rel="stylesheet" type="text/css" href="/css/base.css"/>
        {% endblock %}
    </head>
    <body>
        {% block body %}
            <div id="sidebar">
                {% block sidebar %}
                    <ul>
                        <li><a href="{{ path('homepage') }}">Home</a></li>
                        <li><a href="{{ path('blog_index') }}">Blog</a></li>
                    </ul>
                {% endblock %}
            </div>

            <div id="content">
                {% block content %}{% endblock %}
            </div>
        {% endblock %}
    </body>
</html>

Page sections that can not be overridden by the child templates are defined as Twig block tags. 

The code of blog/layout.html.twig can look like this:

{# templates/blog/layout.html.twig #}
{% extends 'base.html.twig' %}

{% block content %}
    <h1>Blog</h1>

    {% block page_contents %}{% endblock %}
{% endblock %}
The above template that is extended from base.html.twig contains only content blocks. The rest of the content can be overridden from the third level inheritance template such as blog/index.html.twig, which will display a blog index.
{# templates/blog/index.html.twig #}
{% extends 'blog/layout.html.twig' %}

{% block title %}Blog Index{% endblock %}

{% block page_contents %}
    {% for article in articles %}
        <h2>{{ article.title }}</h2>
        <p>{{ article.body }}</p>
    {% endfor %}
{% endblock %}
Three different templates are used by Symfony to create the final when we render blog/index.html.twig using inheritance. Each template uses its unique contents whereas the duplicate HTML structure and contents are left in the parent template.

Code More Efficiently

Templating engines are always helpful when it comes to loading data directly in a website's variable at runtime. There are many templating engines and all of them have their gray areas and white areas.

Twig template provides us many features like reusing Twig templates, output escaping, debugging templates, etc. In this article, we have already seen how inheritance helps in overriding different blocks of template fragments. We have to write code once and can be used again and again. This property makes Twig unique and a great templating engine for web developers.