10 Twig tips and basic features that every developer should know

10 Twig tips and basic features that every developer should know

Twig is a template engine for the PHP programming language. Its syntax originates from Jinja and Django templates. It's an open source product licensed under a BSD License and maintained by Fabien Potencier. The initial version was created by Armin Ronacher.

Despite being one the most full-featured PHP templating engines, Twig is also the fastest one:

Library Time (sec) Memory (Ko) Templates rendered per second
Twig 3 1,190 3,333
PHPTAL 3.8 2,100 2,632
Dwoo 6.9 1,870 1,449
Smarty 2 12.9 2,350 775
Smarty 3 14.9 3,230 671
Calypso 34.3 620 292
eZ Templates 53 5,850 189

Twig is a way to make your php more object and less messy and easy to read.

In this article, we'll share a collection of 10 tips and basics that every developer should know how to manipulate in Twig.

10. Variable declaration

You can assign values to variables inside code blocks. Assignments use the set tag:

{% set fooA = 'foo' %}
{% set fooB = [1, 2] %}
{% set fooC = {
    'foo': 'bar',
    'foo2': 'bar2',
    'group': fooB
} %}

{{ fooC.foo }}
{{ fooC.group[0]}}

Twig allow you to manipulate multiple values in variables. You can handle objects with JSON-Like notation.

You can use a dot (.) to access attributes of a variable if it has (methods or properties of a PHP object, or items of a PHP array), or the so-called "subscript" syntax ([index]).

Note that if a property contains special characters (as dots or minus : myVariable.my-property or myVariable.my.property) you'll need to use the attribute function to access it as normally that syntax would cause a Twig error :

{{ attribute(myVariable, 'my-property') }}

{{ attribute(myVariable, 'my.property') }}

9. String interpolation

Just like PHP does with variables between double quotes : 

<?php

$hello = "Hi";

echo "Inline $hello";
// Outputs : Inline Hi

$myVariable = "Hello {$hello}";
echo $myVariable;
// Outputs: "Hello Hi"

Twig support this feature too and you can even execute inline operations with variable as concatenation and math :

{% set name = 'Hello Buddy' %}
 
{{ "Hi #{name}, how are you today" }}
Hi Hello Buddy, how are you today

{{ "A math operation =  #{(1 + 2) * (4 * 5)}" }}
A math operation =  60

Note: If you want to interpolate use always double quotes (""), if you use single quotes ('') the interpolation will be not used.

8. The loop variable inside a for loop and else tag

Inside of a {%for%} loop block you can access the special variable loop which contains the following properties:

Variable Description
loop.index The current iteration of the loop. (1 indexed)
loop.index0 The current iteration of the loop. (0 indexed)
loop.revindex The number of iterations from the end of the loop (1 indexed)
loop.revindex0 The number of iterations from the end of the loop (0 indexed)
loop.first True if first iteration
loop.last True if last iteration
loop.length The number of items in the sequence
loop.parent The parent context

For example, to check if an item is the last of the iteration, you can use: 

{% for user in users %}
    {% if loop.last %}
        {{"Last user is : " ~ user.username}}
    {% else %}
        {{user.username}}
    {% endif %}
{% endfor %}

Note: The loop.length, loop.revindex, loop.revindex0, and loop.last variables are only available for PHP arrays, or objects that implement the Countable interface. They are also not available when looping with a condition.

Although the {%for%} tag is not an {%if%} tag (you don't say), it supports the {%else%} tag. It becomes automatically a condition if the variable to iterate is empty.

<ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% else %}
        <li><em>no users available</em></li>
    {% endfor %}
</ul>

Although the else tag is useful, you can check if a variable is iterable using the is iterable statement: 

{# evaluates to true if the foo variable is iterable #}
{% if users is iterable %}
    {% for user in users %}
        Hello {{ user }}!
    {% endfor %}
{% else %}
    {# users is probably a string #}
    Hello {{ users }}!
{% endif %}

Sometimes, when using nested loops, you need to access the parent context. The parent context is always accessible via the loop.parent variable. For instance, if you have the following template data:

{% set data = {
    'topics' : {
        'topic1' : ['Message 1 of topic 1', 'Message 2 of topic 1'],
        'topic2' : ['Message 1 of topic 2', 'Message 2 of topic 2'],
    }
}%}

{% for topic, messages in data.topics %}
    * {{ loop.index }}: {{ topic }}
  {% for message in messages %}
      - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
  {% endfor %}
{% endfor %}

And the output should look like :

* 1: topic1
        - 1.1: Message 1 of topic 1
        - 1.2: Message 2 of topic 1
    * 2: topic2
        - 2.1: Message 1 of topic 2
        - 2.2: Message 2 of topic 2

7. Extend a block without remove existing content

If you have an existing block which is located in other file (i.e : base.html.twig) and you need to append in a chield view (i.e child.html.twig) more things to an existent block (in this example more links), you can create a block with the same name in the child view and use the parent() function to retrieve the content of the original block (without the parent function, the block would be only replaced and the content of the parent template wouldn't exist).

{# parent-index.html.twig#}

{%block toolbar -%}
    <a href="path_to_something">
        User Normal Action 1
    </a>
    <a href="path_to_something">
        User Normal Action 2
    </a>
{%endblock%}

{# child-template.html.twig#}

{%block toolbar -%}
    {# The content of the base.html.twig will be retrieved and printed in this inherited view too#}
    {{parent()}}
    
    <a href="path_to_something">
        User Normal Action 3
    </a>
    <a href="path_to_something">
        User Normal Action 4
    </a>
{%endblock%}

{#
    Final Output in the child-template :

    <a href="path_to_something">
        User Normal Action 1
    </a>
    <a href="path_to_something">
        User Normal Action 2
    </a>
    <a href="path_to_something">
        User Normal Action 3
    </a>
    <a href="path_to_something">
        User Normal Action 4
    </a>
#}

If you don't still get it, read the following article to understand completely how parent works.

6. Twig supports Regular Expressions

Twig supports awesome string shortcuts for basic and frequently tasks with strings () and cool functions like starts and ends with :

{% if 'Batman' starts with 'B' %}

{% else%}
    
{% endif %}

{% if 'Batman' ends with 'n' %}

{% else%}
    
{% endif %}

However, not all in the life is starts or ends with. Sometimes there are more complex mechanisms of comparison that you'll need to implement according to your needs and you'll probably solve with Regex.

Twig supports Regular Expressions and you can use them using the matches statement.

{% if "mail@mail.com" matches '/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/' %}
    {{"Is a valid mail"}}
{% else %}
    {{"It doesn't seems to be a valid mail buddy."}}
{% endif %}

5. Control the spaces when you print variables

Twig normally include any space between tags, i.e :

{% for i in 1..10 %}
    {{i}}
{% endfor %}

{# Outputs :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
#}

However, you can prevent this behaviour easily. Twig allow you to clean unnecessary spaces between tags of 2 ways :

  • control whitespace on a per tag level.
  • {% spaceless %} tags.

Control whitespace on a per tag level, allow you to remove spaces from everything you print. To use it include a minus symbol after and before (respectively) of any print tag :

{% for i in 1..10 %}
    {# Note the - symbol at the beginning and the end of the print tag #}
    {{- i -}}
{% endfor %}

{# Outputs :
    12345678910
#}

Note that, if you want you can use only one side instead of both :

<li>    {{- "A string" }}    </li>

{# outputs '<li>no spaces    </li>' #}

<li>    {{ "A string" -}}    </li>

{# outputs '<li>    no spaces</li>' #}

While the spaceless tag allow you to print an HTML block without spaces (which provides a minifier effect) :

{% spaceless %}
    <div>
        <strong>Hey, no easy readable HTML</strong>
    </div>
{% endspaceless %}

{# output will be <div><strong>Hey, no easy readable HTML</strong></div> #}

4. Pretty print a JSON encoded array with twig

Twig supports constants with the constant method.

In PHP you use to pretty print a json string using the JSON_PRETTY_PRINT constant as parameter to the json_encode function.

<?php

$json_string = json_encode($data, JSON_PRETTY_PRINT);

Twig uses json_encode under the hood, therefore you'll still need to use the constant:

{% set data = {
    "Hey": "Ho",
    "What": 12,
    "Value" : true
}%}

{{ data|json_encode()|raw }}

{{ data|json_encode(constant('JSON_PRETTY_PRINT'))}}

{#Outputs :
{# If HTML symbols appears, use the |raw filter after the json_encode function #}

{"Hey":"Ho","What":12,"Value":true}

{
    "Hey": "Ho",
    "What": 12,
    "Value": true
}
#}

3. Modify dates

With Twig you can modify dates easily as you usually did with DateTime objects in PHP.

<?php
$date = new DateTime('2000-12-31');

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
//2001-01-31

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
//2001-03-03
?>

The date_modify filter modifies a date with a given modifier string :

{{ "now"|date_modify('+1 month')|date('Y-m-d')}}

2. Limit string length

There are many cases where you want to truncate a string to prevent unwanted behaviours. To cut a string with a default length use the slice filter.

{% set myTextVariable = "A semi long string"%}

{{myTextVariable|slice(0,10)}}
{# Outputs : A semi lon #}

You can ever use the ternary operator to limit the length of the text and put three dots :

{% set myTextVariable = "A semi long string"%}

{# If the string length > 50, then cut it and append 3 dots .... | otherwise just print the text.#}
{{ myTextVariable|length > 50 ? myTextVariable|slice(0, 10) ~ '...' : myTextVariable }}

{# Which is the same as : #}
{% if myTextVariable|length > 50 %}
    {{ myTextVariable|slice(0, 10) ~ '...'}}
{% else %}
    {{ myTextVariable }}
{% endif %}

1. Use macro

Macros are comparable with functions in regular programming languages. They are useful to put often used HTML idioms into reusable elements to not repeat yourself.

Basically and theoretically: is a type of self-written twig function which outputs custom html according to received parameters.

The macro's needs to be located in another twig file as a good practice and to include it in your current file use the import tag :

{% import "forms.html" as forms %}

However, you can add macro's wherever you are. If you decide to add them in the same file where you are working you just need to the _self path :

{% import _self as forms %}

We can use macro's to print repetitive html blocks as forms. In the following example we'll use a simple macro to print a bootstrap register form :

{% macro input(name, value, type, size) %}
    <div class="form-group">
        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
    </div>
{% endmacro %}

{% import _self as forms %}

<form action="register.php" method="post">
    {{forms.input("frm_username",null,"text")}}
    {{forms.input("frm_password",null,"password")}}
    {{forms.input("frm_email",null,"email")}}
    {{forms.input("frm_birthday",null,"date")}}
    {{forms.input("frm_submit","Register","submit")}}
</form>

And the output should be similar to :

<form action="register.php" method="post">
        <div class="form-group">
        <input type="text" name="frm_username" value="" size="20" />
    </div>

        <div class="form-group">
        <input type="password" name="frm_password" value="" size="20" />
    </div>

        <div class="form-group">
        <input type="email" name="frm_email" value="" size="20" />
    </div>

        <div class="form-group">
        <input type="date" name="frm_birthday" value="" size="20" />
    </div>

        <div class="form-group">
        <input type="submit" name="frm_submit" value="Register" size="20" />
    </div>

</form>

You can see a working macro in the following fiddle :

Frequently asked Twig questions

How to concatenate strings in Twig

In PHP we used to concatenate strings using the . (dot) symbol. In Twig we'll use the ~ (tilde) symbol instead to concatenate strings.

{{"Hello " ~ app.user.username ~ " how are you today?"}}

You can use too the interpolation of strings as mentioned in the 9th point.

How to check if a variable exists

According to the configuration of your Twig environment, you may want to check if a variable exists to prevent errors.

To check if a variable exists use the is defined statement.

{# Uncomment to print the content inside the twig statement
    {% set variable = 12 %}
#}

{% if variable is defined %}
    {{- "Variable exists :" ~ variable -}}
{% endif%}

How to check if a string or array contains a value

To check if an element is inside an array, you can use the in statement.

{% set mytext = "hello how are you today" %}

{% set myarray = ["Hello",12,15,"other string"] %}

{% if "hello" in mytext %}
    {{"'hello' has been found in the string"}}
{% endif %}

{% if "Hello" or 12 in myarray %}
    {{"15 or hello found in array"}}
{% endif %}

Extra

You can test your Twig snippets online in twigfiddle.com which is quick, accessible and faster than create a Twig environment locally.

Have fun !

Become a more social person