2

The basic idea is that I want to group my entries by category, but I want more than just the category title—I need the entire category entry. My previous post alludes to this, but I've since discovered the query I was doing there was returning all related entries regardless of their existance in the block.result entry.

I'm currently doing this—which is exactly what I want, but it doesn't see very efficient... Is there a better way?

{% set results = block.result %}
{% for category, catEntries in results | group('infographicSection.first().title') %}
    {% set fullCat = craft.categories.title(category).first() %}
    <h1 class="{{fullCat.className}}">{{fullCat.title}}</h1>
    <p>{{fullCat.body}}</p>
    <ul>
    {% for entry in catEntries %}

        <li>{{entry.title}}</li>
    {% endfor %}
    </ul>
{% endfor %}
2

Your actual code is pretty effective – the problem is that it's potentially going to result in a lot of queries.

This is a good example of the so-called N+1 query problem, where each iteration over the result set of a single query generates an additional number of queries. Depending on the number of elements involved, this issue can easily result in hundreds of queries, which can be a huge problem for performance.

The solution to the N+1 query problem is eager loading. With eager loading, you explicitly tell Craft which relational data it should include with the first query, unlike with the common "lazy loading" pattern where relational data is queried on a "need to use" basis.

Here's how your code could look, refactored to use eager loading:

{% set results = block.result.find({
    with: ['infographicSection']
}) %}

{% for category, catEntries in results|group('infographicSection[0].title') %}
    {% set fullCat = catEntries[0].infographicSection[0] %}
    <h1 class="{{fullCat.className}}">{{fullCat.title}}</h1>
    <p>{{fullCat.body}}</p>
    <ul>
        {% for entry in catEntries %}
            <li>{{entry.title}}</li>
        {% endfor %}
    </ul>
{% endfor %}

With something like the above, you should see the number of queries drop drastically (remember to turn on devMode in development and you'll see a profiling report in your browser's dev tools console, containing the total number of queries).

  • That's surprisingly easy! Thanks for the great response. Just noticing you're using the [0] syntax—is that a stylistic change or will that also be improving performance? – Darren Wood Nov 15 '16 at 19:59
  • The brackets syntax is actually required with eager loading. The reason is that when you eager load a relational field (such as Entries, Assets or Categories), that field will no longer return an ElementCriteriaModel instance, but a regular ol' array. The .first() method belongs to the ElementCriteriaModel class, and doesn't exist for arrays – hence the [0] instead. For an even better explanation, check out the docs :) – Mats Mikkel Rummelhoff Nov 15 '16 at 20:26

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.