Search code examples
twigtimber

Check if any item has certain value in an Array


I try to get a html structure like this

<h2>Next</h2>
<article> First Entry </article>
<article> Second Entry </article>
....
<h2>Previous</h2>
<article> First Entry </article>
<article> Second Entry </article>
...

So I check the date with

{% for event in events %}
{%- if date(event.band_date) | date('Y-m-d') > 'now' | date('Y-m-d') -%}
Next Stuff
{% endif %}
{% endfor %}

{% for event in events %}
{%- if date(event.band_date) | date('Y-m-d') < 'now' | date('Y-m-d') -%}
Previous Stuff
{% endif %}
{% endfor %}

Now I try to figure out how to get the h2 in there, without being in the loop and duplicate itself, plus if there is no entry within the date-range not showing at all.


Solution

  • An ugly solution would be to use a variable to track whether a heading has been outputted:

    {% set heading_outputted = false %}
    {% for event in events %}
      {% if date(event.band_date)|date('Y-m-d') > 'now'|date('Y-m-d') %}
        {% if not heading_outputted %}
          <h2>Next</h2>
          {% set heading_outputted = true %}
        {% endif %}
    
        <article>
          {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
        </article>
      {% endif %}
    {% endfor %}
    
    {% set heading_outputted = false %}
    {% for event in events %}
      {% if date(event.band_date)|date('Y-m-d') < 'now'|date('Y-m-d') %}
        {% if not heading_outputted %}
          <h2>Previous</h2>
          {% set heading_outputted = true %}
        {% endif %}
    
        <article>
          {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
        </article>
      {% endif %}
    {% endfor %}
    

    Example: https://twigfiddle.com/0kdp0u


    A better solution would be to have the logic in PHP, for example in the controller, or whatever might be the most suitable location in your project. If you pass variables next_events and previous_events to Twig, the view will be much cleaner:

    {% if next_events is not empty %}
      <h2>Next</h2>
      {% for event in next_events %}
        <article>
          {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
        </article>
      {% endfor %}
    {% endif %}
    
    {% if previous_events is not empty %}
      <h2>Previous</h2>
      {% for event in previous_events %}
        <article>
          {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
        </article>
      {% endfor %}
    {% endif %}
    

    Example: https://twigfiddle.com/cvka3g


    One more solution using the filter filter and the loop variable:

    {% for event in events|filter(event => date(event.band_date)|date('Y-m-d') > 'now'|date('Y-m-d')) %}
      {% if loop.first %}
        <h2>Next</h2>
      {% endif %}
    
      <article>
        {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
      </article>
    {% endfor %}
    
    {% for event in events|filter(event => date(event.band_date)|date('Y-m-d') < 'now'|date('Y-m-d')) %}
      {% if loop.first %}
        <h2>Previous</h2>
      {% endif %}
    
      <article>
        {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
      </article>
    {% endfor %}
    

    (This solution was inspired by the answer by @Seba.)

    Twigfiddle has disabled the filter filter for security reasons, so no runnable example for this solution.

    Note that the following syntax doesn't work in Twig 3:

    {% for event in events if date(event.band_date)|date('Y-m-d') > 'now'|date('Y-m-d') %}
      {% if loop.first %}
        <h2>Next</h2>
      {% endif %}
    
      <article>
        {{ event.band }} - {{ event.band_date|date('Y-m-d') }}
      </article>
    {% endfor %}
    

    It does work in Twig 2 (example: https://twigfiddle.com/zau2sf), but you'll get the following deprecation notice:

    Using an "if" condition on "for" tag in "main.twig" at line 1 is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).


    I would personally go with the second solution since the Twig template is the clearest. If that's not possible, I would go with the third solution. The first solution is so messy that I wouldn't choose it.