May 30, 2017

How to iterate a list in batches using Twig (or plain PHP)

Recently I had to loop through a series of elements in a WordPress template (thanks Timber), which happened to be using Bootstrap.

If you ever used Bootstrap, you are familiar with the following markup:

<div class="row">
  <div class="element col-xs-4">
    Stuff
  </div>
  <div class="element col-xs-4">
    Stuff
  </div>
  <div class="element col-xs-4">
    Stuff
  </div>
</div>
<div class="row">
  ...
</div>

The problem here is very common. You have to:

  • Loop every 3/4/n items.
  • Insert a separator or whatever every 3/4/n items.

You can achieve this with modulo (divisibleby) and checking if the loop index is last and other small details. However, I think that solution is not super clear. Twig introduced the batch filter in version 1.12.3, where you can make this much less cumbersome.

Adapting the current example and their docs, this is how we achieve a clean loop:

{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}

{% for row in items|batch(3, 'No item') %}
  <div class="row">
    {% for column in row %}
    <div class="element col-xs-4">
      {{ column }}
    </div>
    {% endfor %}
  </div>
{% endfor %}

Much better.

Plain PHP

I later learnt that PHP has a built-in function that handles this case. It’s called array_chunk which, of course, splits an array into chunks or batches.

<?php foreach (array_chunk($photoList, 2) as $photos): ?>
  <div class="grid-row">
  <?php foreach ($photos as $key => $photo): ?>
    <div class="grid-overlay__img-container">
      <div class="grid-overlay__img" data-src="<?= $photo['url_full'] ?>"></div>
    </div>
  <?php endforeach; ?>
  </div>
<?php endforeach; ?>

Edit 19 Jul: Added plain PHP version.