Buy

The for “loop” and inline “if” Syntax

The for “loop” and inline “if” Syntax

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 divisibleby 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 divisibleby(3) %}
            </div><div class="row">
        {% endif %}
    {% endfor %}
</div>

Note

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?

The Magical loop Variable

Twig comes to the rescue here and lets us say loop.index.

{% for product in products %}
    {# ... #}

    {% if loop.index is divisibleby(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 divisibleby(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!

The for-else tag

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.

The inline if Syntax

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.

Leave a comment!

  • 2015-06-18 weaverryan

    You're absolutely right :) - I like using default like this too.

  • 2015-06-18 Victor Bocharsky

    We could also to use default filter instead of is defined test, for example:


    {{ backgroundColor | default('lightBlue') }}

    This is more readable and elegant solution, imo.