1

I'm confused, I have ended up with 2 similar queries in a template after converting them from Craft 2.

{% set events = craft.entries()
    .section('events')
    .eventDate('>' ~ now|date('Y-m-d H:i:s'))
    .limit(3)
    .orderby('eventDate')
    .all()
%}
{% for entry in events %}
...
{% endfor %}

and

{% set entries = craft.entries()
    .section('news')
    .orderBy('postDate desc')
    .limit('4')
    .with(['imageGallery'])
%}
{% for entry in entries.all() %}
...
{% endfor %}

Both work without any errors reported but which one is better or correct?

2 Answers 2

4

Both approaches are valid. In your first example, you could have moved .all() into the for-loop; and in the second example, you could have moved .all() out of the for-loop, and into the {% set entries = ... %} statement.

Until you call .all(), you are working with an entry query, and after you call .all(), you’re working with an array of the actual entries (the query results).

So which approach is better depends on what else you’re doing in your template.

For example, if you need to output the total number of entries, you could do this:

{% set entryQuery = craft.entries()
    .section('news')
%}

{% for entry in entryQuery.all() %}
    ...
{% endfor %}

Total: {{ entryQuery.count() }}

But it would result in two database queries getting executed – once to get the entries for the for-loop, and a second query to get the total matching entries.

If you know you’re going to need that info, then it would be better to just fetch the entries ahead of time, and check the length of the array instead:

{% set entries = craft.entries()
    .section('news')
    .all()
%}

{% for entry in entries %}
    ...
{% endfor %}

Total: {{ entries|length }}

Regardless of the approach you decide to go with, you might want to make sure you are using semantec variable names. In your two examples you have events (an array of actual event entries) and entries (an entry query).

I would say that if you are not going to call .all() when defining your entries variable, then you might want to rename that to either entryQuery or query, just so it’s easier to remember what sort of variable you’re working with later on in the template.

{% set entryQuery = craft.entries()
    .section('news')
    .orderBy('postDate desc')
    .limit('4')
    .with(['imageGallery'])
%}

{% for entry in entryQuery.all() %}
    ...
{% endfor %}
3
  • Damn, tied... you clearly put in a bit more effort. :)
    – Lindsey D
    Commented Aug 13, 2018 at 21:27
  • Thanks for the detailed response Brandon. It helps with understanding the process, which is something I find very hard with Craft. Sometimes I long for the simplicity of EE templates and the lack of complexity/code needed for simple thngs, but I do recognise that Craft allows me to do much more complicated things that I couldn't do in EE. It would be nice if there were more Craft 3 complete template examples for common tasks, like the ones at the bottom of the official docs an on craftcookbook.net
    – Paul Frost
    Commented Aug 14, 2018 at 8:07
  • Yeah, we’re slowly working to add better examples to the docs. I just rewrote the Element Queries page to hopefully be a little more clear, and with more usable examples. Commented Aug 14, 2018 at 14:08
2

They are both correct.

In your first example, events is an array of entries. In your second example, entries is an Element Query. As soon as you apply the .all() to your Element Query, it converts that into an array of entries.

The only two things you need to know about .all() is:

  • It must exist (or use .one())
  • It must be last

That's it!

1
  • Brad wins due to the detailed explanation which helps me understand what's going on, but you get a special mention for the easy to remember rules.
    – Paul Frost
    Commented Aug 14, 2018 at 7:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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