If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Let's give ourselves a challenge! Our products are printing out a bit weird
right now because they're floating but not breaking correctly. To fix this,
we need to wrap every three products in their very own row
div.
To do this, we can use a divisible by() test to see if the item number we're on is divisible by three:
<div class="row">
{% for product in products %}
<div class="span4">
{# ... #}
</div>
{% if loopNumber is divisible by(3) %}
</div><div class="row">
{% endif %}
{% endfor %}
</div>
Just like functions and filters, sometimes a "test" also takes one or more arguments.
When we refresh, Twig is sad because the loopNumber
variable is undefined.
Yep, that's totally my fault, I made up that variable out of thin air. So
how can we figure out how many items into the loop we are?
Twig comes to the rescue here and lets us say loop.index
.
{% for product in products %}
{# ... #}
{% if loop.index is divisible by(3) %}
</div><div class="row">
{% endif %}
{% endfor %}
When we refresh, things work awesomely! So where did this magical loop variable come from? Normally in Twig, we have access to a few variables that were passed to us and that's it. If we use an undefined variable, we see an error.
This is all 100% true. But when we're inside a for tag, we magically have
access to a new variable called loop. loop.first
and loop.last
tell us if this is the first or last item in the collection while loop.index
counts up 1, 2, 3, 4 and so on for each item. Twig has a lot of really slick
features like this, which you can find out by reading further into its docs.
In fact, to avoid an extra row being added if we have exactly 3, 6 or 9 objects,
let's not print a new row
if we're on the last item:
{% for product in products %}
{# ... #}
{% if loop.index is divisible by(3) and not loop.last %}
</div><div class="row">
{% endif %}
{% endfor %}
And not that it matters for Twig, but let's also move our "even" products message into its own row where it belongs.
{# templates/homepage.twig #}
{# ... after the for loop #}
{% if products|length is even %}
<div class="row">
<div class="span12">
There is an even number of products! OMG!
</div>
</div>
{% endif %}
When we refresh, everything looks good and clean!
While we're talking about cool for
loop features, let's see another one:
the for-else
trick. Instead of seeing if products
is empty, we can
add an else
tag inside of the for
loop.
{% for product in products %}
{# ... #}
{% else %}
<div class="alert alert-error span12">
It looks like we're out of really awesome-looking penguin clothes :/.
</div>
{% endfor %}
If products
is empty, it skips the for
loop and calls the else
section instead. When we try it, it still works great.
Finally, let's see a really short syntax you can choose to use instead of
the classic if
tag. Head back to the banner template where we're setting
the backgroundColor
variable if it's not set and then printing it. Let's
remove all of this and instead put all the logic in the "say something" block:
<div class="well" style="background-color: {{ backgroundColor is defined ? backgroundColor : 'lightBlue' }};">
{# ... #}
</div>
You may be familiar with this syntax from another language, but if you're
not, don't worry! It looks odd, but is really easy. The first part is a condition
that returns true or false, just like an if statement. If it's true, the first
variable backgroundColor
is printed. If it's false, the second string
lightblue
is printed. The result is identical to before.
Hello beautiful people. I wonder why in the if statement that is just before the for loop we put the closing div tag before the div open tag class row. Thanks for your answers.
`
{% if loop.index is divisibleby(3) %}
</div><div class="row">
{% endif %}
`
Hey Beniamin T.
That's because we have to close the first "row" element before adding another row element into the layout. But it looks weird, I know :)
Cheers!
Hey Dylan Delobel ☕!
Great question :). And I don't have a good reason - the batch filter is great! Actually, when we created this tutorial, the batch filter either didn't exist or was quite new. So, it's an excellent tool.
Cheers!
Is the syntax of the "divisible by" test `divisible by` or `divisibleby` ?
(i.e. does it matter if there is a space between the words?)
The dox and the example don't match...
Hey Goz,
Yes, it does matter! Well, depends on Twig version. In Twig 2.x only "divisible by()" works, but in 1.x both syntax were allowed, but "divisible by()" were added as an alias since 1.14.2. See related docs:
https://twig.symfony.com/do...
https://twig.symfony.com/do...
Cheers!
// composer.json
{
"require": []
}
We could also to use <a href="http://twig.sensiolabs.org/doc/filters/default.html">default</a> filter instead of
is defined
test, for example:This is more readable and elegant solution, imo.