for

Loop over each item in a sequence or a mapping. For example, to display a list of users provided in a variable called users:

<h1>Members</h1>
<ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% endfor %}
</ul>

Note

A sequence or a mapping can be either an array or an object implementing the Traversable interface.

If you do need to iterate over a sequence of numbers, you can use the .. operator:

{% for i in 0..10 %}
    * {{ i }}
{% endfor %}

The above snippet of code would print all numbers from 0 to 10.

It can be also useful with letters:

{% for letter in 'a'..'z' %}
    * {{ letter }}
{% endfor %}

The .. operator can take any expression at both sides:

{% for letter in 'a'|upper..'z'|upper %}
    * {{ letter }}
{% endfor %}

Tip

If you need a step different from 1, you can use the range function instead.

Iterating over Keys

By default, a loop iterates over the values of the sequence. You can iterate on keys by using the keys filter:

<h1>Members</h1>
<ul>
    {% for key in users|keys %}
        <li>{{ key }}</li>
    {% endfor %}
</ul>

Iterating over Keys and Values

You can also access both keys and values:

<h1>Members</h1>
<ul>
    {% for key, user in users %}
        <li>{{ key }}: {{ user.username|e }}</li>
    {% endfor %}
</ul>

Iterating over a Subset

You might want to iterate over a subset of values. This can be achieved using the slice filter:

<h1>Top Ten Members</h1>
<ul>
    {% for user in users|slice(0, 10) %}
        <li>{{ user.username|e }}</li>
    {% endfor %}
</ul>

Iterating over a String

To iterate over the characters of a string, use the split filter:

<h1>Characters</h1>
<ul>
    {% for char in "諺 / ことわざ"|split('') -%}
        <li>{{ char }}</li>
    {%- endfor %}
</ul>

Adding a Condition

Skipping items during an iteration can be done in several ways:

  • Using a filter filter:

    {% for user in users|filter(user => user.active) %}
        - {{ user.username }}
    {% endfor %}
    

    The items are filtered before the loop starts (the loop.index will not be incremented for filtered items).

  • Using an if condition after for:

    {% for user in users if user.active %}
        - {{ user.username }}
    {% endfor %}
    

    The items are filtered during the loop; all items are iterated (the loop.index will also increment for filtered items). As a consequence, be warned that the loop.last variable might never be set to true if the last item is skipped and loop.length returns the length of the unfiltered sequence/mapping.

    This is just a convenient shortcut for using an if condition inside the for body (both are equivalent):

    {% for user in users %}
        {% if user.active %}
            - {{ user.username }}
        {% endif %}
    {% endfor %}
    

    It’s recommended to use the filter filter except for when you need to filter based on a variable that changes in the body of the loop:

    {% set users = ['Thomas', 'Lucas', 'Fabien', 'Hélène'] %}
    {% set stopOnFabien = false %}
    {% for user in users if not stopOnFabien %}
        - {{ user }}
        {% set stopOnFabien = user == 'Fabien' %}
    {% endfor %}
    

    Or when you need to use the loop variable in the condition:

    {% for user in users if loop.length != 5 %}
        - {{ user }}
    {% endfor %}
    

The else Clause

If no iteration took place because the sequence was empty, you can render a replacement block by using else:

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

Recursive Loops

To use loops recursively, pass the iterable you want to recurse to the loop() function; the following example shows how to use it for a recursive sitemap:

<ul class="sitemap">
{%- for item in sitemap %}
    <li>{{ item.title }}
    {%- if item.children -%}
        <ul class="submenu">{{ loop(item.children) }}</ul>
    {%- endif %}</li>
{%- endfor %}
</ul>

The loop Object

Inside of a for loop block, a loop object exposes some information about the current loop iteration.

loop Variables

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

loop.previous

The value from the previous iteration (null for the first iteration)

loop.next

The value from the next iteration (null for the last iteration)

loop.depth

Deep level of a recursive loop (1 indexed)

loop.depth0

Deep level of a recursive loop (0 indexed)

Note

When the underlying PHP iterator is not countable, the loop.length, loop.revindex, and loop.revindex0 variables are not available and a RuntimeException is thrown if you try to use them.

Here is an example on how to use the index variable:

{% for user in users %}
    {{ loop.index }} - {{ user.username }}
{% endfor %}

Use loop.previous and loop.next to access the previous or next values:

{% for value in values %}
    {% if not loop.first and value > loop.previous %}
        The value just increased!
    {% endif %}
    {{ value }}
    {% if not loop.last and loop.next > value %}
        The value will increase even more!
    {% endif %}
{% endfor %}

loop.previous is null when called at the first item, and loop.next is null when called at the last item.

loop Functions

The loop object also exposes some functions:

Function

Description

loop.cycle()

Cycle over a sequence of values

loop.changed()

True if previously called with a different value or if not called yet

loop()

Allows to iterate over a nested sequence/mapping

Use loop.cycle() to cycle among a list of values:

{% for row in rows %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}

Use loop.changed() to check if the value has changed since the last call:

{% for entry in entries %}
    {% if loop.changed(entry.category) %}
        <h2>{{ entry.category }}</h2>
    {% endif %}
    <p>{{ entry.message }}</p>
{% endfor %}

Accessing the outer loop in Nested Loops

When using nested loops, you can access the outer loop object by storing it in a variable before entering the inner loop.

For instance, if you have the following template data:

$data = [
    'topics' => [
        'topic1' => ['Message 1 of topic 1', 'Message 2 of topic 1'],
        'topic2' => ['Message 1 of topic 2', 'Message 2 of topic 2'],
    ],
];

And the following template to display all messages in all topics:

{% for topic, messages in topics %}
    * {{ loop.index }}: {{ topic }}
    {% set outer_loop = loop %}
    {% for message in messages %}
        - {{ outer_loop.index }}.{{ loop.index }}: {{ message }}
    {% endfor %}
{% endfor %}

The output will be similar to:

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

Within the inner loop, the outer_loop variable can be used to reference the outer loop object.