Objectives & prerequisites
- Explain how to loop over a list of items.
- Name the 2 types of list you can use in a loop.
- Loop over a partial list.
- Control whitespace in Twig output.
- Compare Twig's
forsyntax with PHP'sforeachsyntax. - List 3 useful loop variables
- Working with {{ }} and {% %} tags.
- Working with conditional operators in Twig.
- Basic programming concepts (variables, operators, control structures)
Overview
Twig distinguishes two types of lists (arrays): sequences and mappings.
Sequences are 'simple' lists of values where each element can be addressed via a numerical index. Mappings are lists of key/value pairs where each value can be addressed via its key.
It is extremely common to have to loop through lists to find elements that match a certain condition, or to perform an action on each element.
To loop through lists you can use Twig's {% for %} ... {% endfor %} syntax.
Note: Looping through lists is also called iterating, and each loop is also called an iteration.
Example 1A
To print all the values in a compound variable, you must loop through the list and print each value, one after the other:
{% set friends = [ 'Alex', 'Chris', 'Latisha' ] %}
{% for friend in friends %}
{{ friend }}
{% endfor %}Output:
Alex
Chris
LatishaExplanation:
- Loop over the elements in the list.
- At the start of each loop (iteration), copy the value of the current element into a temporary variable (the iterator)
- Print the friend variable.
- Perform the next loop.
During the first loop, friend will contain "Alex".
During the second loop, friend will contain "Chris".
During the third loop, friend will contain "Latisha".
Note: we chose to name the iterator friend, but we could have also called it item or even x:
{% for x in friends %}
{{ x }}
{% endfor %}However, it is very common to give a list variable a plural name (friends, items, messages) and then give the iterator the single name (friend, item, message):
{% for item in items %}
{{ item }}
{% endfor %}This convention of using plural words for lists and single words for variables that contain a single item keeps your code readable and helps avoid errors due to unclearly named variables.
Example 1B
Let's repeat Example 1, but format the output as an unordered HTML list - a common requirement
<ul>
{% for friend in friends %}
<li>{{ friend }}</li>
</ul>
Output:
<ul>
<li>Alex</li>
<li>Chris</li>
<li>Latisha</li>
<ul>Twig loops vs PHP loops
PHP has a similar control structure that inspired the Twig loop syntax: foreach loops:
<?php
foreach ($friends as $friend) {
print ($friend);
}
Loops with key/value pairs
When you construct a PHP foreach loop, you can loop through an array's values, as we did in the previous examples. However, you can also access they array's keys while you're inside the loop.
Consider the following PHP snippet and its Twig equivalent:
PHP Example
$users = [
1 => 'Admin',
42 => 'Sarah Connor',
99 => 'Zhang Bei',
];
foreach ($users as $id => $username) {
print("$username has ID: $id" . "\n");
}
Twig Example
{% set users = {
1: 'Admin',
42: 'Sarah Connor',
99: 'Zhang Bei'
}
%}
{% for id, username in users %}
{{ username }} has ID: {{ id }}
{% endfor %}
Output for both
Admin has ID: 1
Sarah Connor has ID: 42
Zhang Bei has ID: 99
Accessing mappings
Let's look at how to loop through a sequence where each element is a mapping:
{% set user1 = {'id': 57, 'name': 'alice'} %}
{% set user2 = {'id': 12, 'name': 'bob'} %}
{% set user3 = {'id': 23, 'name': 'charles' } %}
{% set users = [ user1, user2, user3 ] %}
{% for user in users %}
{{ user.id }} | {{ user.name }}
{% endfor %}
Output:
57 | alice
12 | bob
23 | charles
During each loop, the iterator (user) contains the current element in the loop. Since we are looping through a sequence of mappings, user will contain a mapping each time, and you can access all of its properties the same way you would access them if they were working directly with the user1, user2, and user3 variables.
Loop variables
When you're inside a loop, you have access to a special variable named loop that keeps track of the number of items you're looping over, whether this is the first or last element of the list, and a few other things.
The most useful ones are:
loop.index
The current iteration of the loop (index of first element is 1).
loop.index0
The current iteration of the loop (index of first element is 0).
loop.first
True if first iteration.
loop.last
True if last iteration.
loop.length
The number of items in the list.
For a full list of loop properties, refer to the Twig documentation on the loop variable
Example
Let's modify the previous example and give each value a sequential number using the loop.index variable:
{% set users = {
1: 'Admin',
42: 'Sarah Connor',
99: 'Zhang Bei'
}
%}
{% for id, username in users %}
user #{{ loop.index }} | {{ username }} (ID: {{ id }})
{% endfor %}
Output:
user #1 | Admin (ID: 1)
user #2 | Sarah Connor (ID: 42)
user #3 | Zhang Bei (ID: 99)
Partial loops
In some scenarios it is necessary to loop over part of a list instead of the complete list. Here are two ways to do this:
Example 1: loop.index
Print the first 3 values in a list by checking which loop you're in each time:
{% set colours = ["red", "green", "blue", "white", "black", "purple", "orange"] %}
{% for colour in colours %}
{% if loop.index0 < 3 %}
{{ colour }}
{% endif %}
{%endfor %}
Output:
red
green
blue
This is what we wanted to achieve, but notice that the whitespace in the Twig code has been preserved in the output.
Twig provides special characters to control whitespace in its output: - and ~.
See the Symfony Blog article Better whitespace control in Twig templates to learn more about whitespace control options.
Example 2: slice
Print the first 3 values in a list by reducing the number of values using the slice filter:
{% set colours = ["red", "green", "blue", "white", "black", "purple", "orange"] %}
{% for colour in colours | slice(0, 3) %}
{{ colour }}
{%endfor %}
Output:
red
green
blueExplanation:
Instead of iterating over all the values, we iterate over a slice or subset of the values by indicating where we want to start (index 0) and how many items we want to process (3).
Refer to the slice docs for more options and examples.
Looping a specific number of times
So far we've covered how to loop over all or some of a list's values. You can also loop a specific number of times using, for example, the range() function:
{% for i in range(0, 5) %}
{{ i }}
{% endfor %}
Output
0
1
2
3
4
5
Activity 1
Remove all the whitespace in front of the items in Example 1 of Partial loops using the - and or ~ whitespace control characters. Use the Symfony blog post for reference.
Activity 2
Given a list of 5 items, use a loop to print items 3 and 4.
Activity 3
Given a list of 5 items, use a loop to print the last 3 items.
Activity 4
Given a list of 5 items, use a loop but only print the first item.
Provide three different ways:
- once using slice
- once using a loop variable
- once using a different loop variable
Summary
- Loop over lists using
{% for %} ... {% endfor %}. - Inside each loop, use the iterator to access the current value.
- Inside each loop, use the
loopvariable for information about the current loop. - Use
-and~if you need to control whitespace in the output. - Use the slice filter to only loop over part of a list.