1

I have a section with trainings. In each training entry there's a matrix field where we add a new block with a training date, location, etc. So each training has multiple matrix blocks.

When I run an entry query and get all the matrix date fields inside of them, I can output it like this:

  • Training 1
    • October 1, 2020
    • October 5, 2020
  • Training 2
    • October 5, 2020
    • October 10, 2020
  • Training 3
    • October 7, 2020
    • October 10, 2020

Now I would like to group and order the trainings by date like this:

  • October 1, 2020
    • Training 1
  • October 5, 2020
    • Training 1
    • Training 2
  • October 7, 2020
    • Training 3
  • October 10, 2020
    • Training 2
    • Training 3

I'm close but can't get the dates sorted the right way. The template currently outputs:

  • October 1, 2020
    • Training 1
  • October 5, 2020
    • Training 1
    • Training 2
  • October 10, 2020
    • Training 2
    • Training 3
  • October 7, 2020
    • Training 3

The code I have right now:

{# Query entries that have training dates #}
{% set allEntries = craft.entries
.section('trainings')
.trainingDates(':notempty:')
.all() %}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates.all() %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date #}
<ul>
    {% for startDate in startDates %}
        <li>
            <h2>{{ startDate }}</h2>
            <ul>
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates.all() %}
                        {% if date.startDate | date('d mm Y') == startDate %}
                            <li><a href="{{ entry.url }}" title="{{ entry.title }}">{{ entry.title }}</a></li>
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

Any idea on how to sort the array by date? And this can probably done much easier. If you have any tips, I would love to hear from you! :-)

2
  • What type of field is trainingDates? – James Smith Sep 23 '20 at 9:00
  • @JamesSmith trainingdates is a maxtrix field. – Rinus Sep 23 '20 at 13:04
2

The quick and easy answer would be to just add the Twig sort filter to your startDates array. e.g:

{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Merge all training dates in an array #}
{% set startDates = [] %}
{% for entry in allEntries %}
    {% for training in entry.trainingDates %}
        {% if training.startDate | date('d mm Y') not in startDates %}
            {% set startDates = startDates | push(training.startDate | date('d mm Y')) %}
        {% endif %}
    {% endfor %}
{% endfor %}

{# Show all trainings grouped by date#}
<ul>
    {% for startDate in startDates|sort %}
        <li>
            <h2>{{ startDate }}</h2>
            <ul>
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate | date('d mm Y') == startDate %}
                            <li><a href="{{ entry.url }}" title="{{ entry.title }}">{{ entry.title }}</a></li>
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

(I've also added eager loading to improve performance).

You could potentially attack the problem from the opposite direction by using a matrixblock element query like this:

{# Query matrix blocks that have training dates #}
{% set matrixBlocksQuery = craft.matrixBlocks().field('trainingDates') %}
{% set uniqueDatesSorted = matrixBlocksQuery.all()|map(block => block.startDate|date('d mm Y'))|unique|sort %}

{# Query entries that have training dates #}
{% set allEntries = craft.entries
    .section('trainings')
    .with('trainingDates')
    .trainingDates(':notempty:')
    .all()
%}

{# Show all trainings grouped by date#}
<ul>
    {% for startDate in uniqueDatesSorted %}
        <li>
            <h2>{{ startDate }}</h2>
            <ul>
                {% for entry in allEntries %}
                    {% for date in entry.trainingDates %}
                        {% if date.startDate|date('d mm Y') == startDate %}
                            <li><a href="{{ entry.url }}" title="{{ entry.title }}">{{ date.id }}{{ entry.title }}</a></li>
                        {% endif %}
                    {% endfor %}
                {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

...but to be honest I think I prefer your approach, as it's more readable and doesn't require an extra .all() query to get the dates

1
  • Thanks a lot! Adding sort fixed it. And thanks for the matrix block query example. Might be handy for future projects. – Rinus Sep 23 '20 at 19:16
1

I loaded a database with 50+ trainings and the sort was wrong again. And I figured out why.

I pushed all the dates in an array with the date format like date('d mm Y'). Dates would be 03 november 2020, 17 october 2020, 01 december 2020, etc. The templates used the first two numbers to sort. So the entries started with december, then november and then october.

I changed the date format to date('c') and now all the entries are sorted fine. Later on in the loop I displayed the startdate with {{ startDate | date("d mm Y") }}.

So anyone trying to do the same, date('c') does the trick.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.