6

On my home page template I include a couple of extracts from single entries and I'm not sure which is the best/most efficient way to call them.

{% for entry in craft.entries.section('information').slug('about') %}
    <h2>{{ entry.title }}</h2>
    {{ entry.summary }}
{% endfor %}

or

{% set entry = craft.entries.id(13).first() %}
<h2>{{ entry.title }}</h2>
{{ entry.summary }}

They both generate the same amount of queries in the Chrome dev tools profile summary report, but I don't understand why, as the second looks like it's more specific and not a loop.

4 Answers 4

8

Regardless of the number of elements involved, Craft will return an iterable ElementCriteriaModel whenever you query for content (e.g. via craft.entries, or using relational FieldTypes like Assets, Entries or Categories).

This means that even if you know that there'll only be a single element returned (e.g. when you query for a Single section entry, or have a relational field restricted to a single element) using a for loop to actually access that element is totally viable and whether to actually use a loop or not in such cases, is mostly a matter of personal preference.

Looping on single-element query results may seem counter-intuitive, although you're not actually looping on a single element, but the ElementCriteriaModel (which just happens to "contain" a single element and not several). Personally I tend to only use loops when I actually need loops (i.e. there is or could be more than one element returned), but its up to you.

In any case, beyond personal preference there are a few minor differences to each approach.

Using a {% for %} loop, it's easy to render alternative content if the query doesn't return anything, by adding an {% else %} statement:

{% for entry in craft.entries.section('mySingleSection') %}
    <h1>{{ entry.title }}</h1>
{% else %}
    <p>Sorry, the entry wasn't found!</p>
{% endfor %}

Using .first() and {% set %}, you should – in most cases – add an {% if %} statement testing the value returned, to avoid template errors if the query doesn't return a valid element:

{% set entry = craft.entries.section('mySingleSection').first() %}
{% if entry %}
   <h1>{{ entry.title }}</h1>
{% endif %}

Also using .first() and {% set %}, it's easy to declare fallback data if the query doesn't return anything, using the |default filter – this may negate the need for an {% if %} statement:

{% set imageUrl = entry.assetsField.first().url|default('/assets/fallback.jpg') %}
<img src="{{ imageUrl }}" />

Performance-wise, I doubt there'll be any real-world difference between the two methods, with only a single element involved.

As for the query part of your question; even if you're using a for loop, Craft won't run the craft.entries query in your example more than once – which is why you're seeing the same amount of queries in your profiling report. When talking about queries and performance, it's however worth noting that both the amount of queries and the efficiency of those queries are relevant. In your example, the first query will probably be a tiny, tiny bit faster than the second (Gustavs' answer explains why).

1
  • 1
    Thanks Mats, that's a useful summary of example uses. I hadn't thought about using the default filter in case it was empty. It's the fact that there umpteen ways to do everything that I find it hard to know what is the BEST method.
    – Paul Frost
    Commented Nov 28, 2016 at 21:25
5

Both are essentially the same query, the only thing that differs is the WHERE statement. The ID approach will always be the most efficient, since it uses the primary key index, thus finding the result faster.

You could also do the first example the same way you did the second one:

{% set entry = craft.entries.section('information').slug('about').first() %}
<h2>{{ entry.title }}</h2>
{{ entry.summary }}
1
  • I realised that but as you wrote I expected the ID to be the most efficient
    – Paul Frost
    Commented Nov 28, 2016 at 21:04
2

Working with singles you can use them both. However, the set has the advantage that you can apply multiple filters etc. on it afterwards. Especially handy with more complex designs and when working with channels etc.

1
  • 1
    I do use set for some complex filters on some entries already and they are easy to build up.
    – Paul Frost
    Commented Nov 28, 2016 at 21:18
0

Personally I would pick the For loop to keep my code look equal plus you are looping over an entry so in my opinion you should use FOR

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.