1

This is a single entry page. I have an assets field of imageGallery which allows multiple images to be uploaded but it could also be empty. I want to use the first image at the top of the page (if one exists) then show the rest of the images (if there is more than one) excluding the first one after the bodyText field. I'm also loading a transform named 'gallery' if the original image is larger than the transform. This is the code I have now (simplified for clarity):

{% set titleImage = entry.imageGallery[0] ?? null %}

{% if titleImage|length %}
    {% if titleImage.getWidth()>titleImage.getWidth('gallery') %}
        <img src="{{ titleImage.url('gallery') }}"
    {% else %}
        <img src="{{ titleImage.url }}"
    {% endif %}
{% endif %}

{{ entry.bodyText }}

{% if entry.imageGallery.count() > 1 %}
    {% for image in entry.imageGallery.offset(1).all() %}
        {% if image.getWidth()>image.getWidth('gallery') %}
            <img src="{{ image.url('gallery') }}"
        {% else %}
            <img src="{{ image.url }}"
        {% endif %}
    {% endfor %}
{% endif %}

How could this be impoved on for faster loading?

  • 1
    Just as an additional note besides @mats great answer. The count() function executes the query and counts the results, imageGallery[0] is a deprecated way to execute the query and you'll finally execute it a 3rd time with .offset(1).all(). So you execute 2 useless queries that could be avoided – Robin Schambach Aug 14 '18 at 13:47
3

Disclaimer: While eager-loading will almost always bring down the total number of queries executed, it's important to note that having less queries won't always equal better performance. When optimising a page with eager-loading, you should take care to observe the actual time Craft spends on database queries – which is the metric that matters.

For Craft 3, you should enable the Debug Toolbar, which will tell you exactly how many milliseconds Craft is using for database queries. Eager loading is not a silver bullet, and you should note the time spent on database queries with and without eager loading, to determine if it's actually increasing performance or not. You can enable the debug toolbar from your Craft user's profile page inside the Control Panel.

For Craft 2, you'll need to enable devMode, crack open your browser's dev tools console and expand the *Profiling Summary Report*. Unfortunately, AFAIK there's no record of the time spent on queries specifically, but you can gauge it by looking at the Time property (which will report the total time Craft spent resolving the request, including database queries). Obviously, the lower this number is, the better.

Furthermore, before worrying about eager loading, you should make sure that your template is properly optimised without it. The most important thing is to make sure that you're not doing multiple, redundant queries.

In the code example above, you're first executing two queries on the entry.imageGallery() field outside the {% for %} loop, and then 1 additional query per image inside the {% for %} loop. In other words, if you added 100 images to the galleryImages field, you'd execute a total of 102 queries – which is totally unnecessary.

By caching the array of Assets returned by .all() to a variable, you can get away with a single query:

{# Get all images in the field #}
{% set galleryImages = entry.galleryImages.all() %}

{# Set the `titleImage` to the first image #}
{% set titleImage = galleryImages[0] ?? null %}

{% if titleImage %}
    ...
{% endif %}

{{ entry.bodyText }}

{# Use the |slice filter to remove the first image – i.e. the title image – from the Assets array, and then print the rest of the images #}
{% set galleryImages = galleryImages|slice(1) %}

{% if galleryImages|length %}
    {% for image in galleryImages %}
        ...
    {% endfor %}
{% endif %}

Note the use of the |slice filter – this is used to remove the first index (i.e. the first image) from the array, after caching it to a variable titleImage. Don't worry, the |slice operation won't throw an error, even if the galleryImages array is empty.

With the above optimisation, your X number of queries have been reduced to a single query – and at that point, you probably wouldn't see any performance improvements by adding eager loading.

But, if you wanted to try it – here's how it could be done (this would replace the first line in the above example):

{% do craft.app.elements.eagerLoadElements(
    className(entry),
    [entry],
    ['galleryImages']
) %}

The above statement should go at the very top of your template – and if you do this, make sure to remove the reference to .all() in the first line of the first example (by eager loading the Assets field, the entry.galleryImages property is already an array and hence has no all() method).

Again, the above is probably not useful if you do the optimisations to bring down your query count to 1 – but, it could be useful if you had multiple relational fields you wanted to query, i.e.

{% do craft.app.elements.eagerLoadElements(
    className(entry),
    [entry],
    ['galleryImages', 'entriesField', 'categoryField']
) %}

For more info on eager loading, check out this excellent article.

|improve this answer|||||
  • 2
    So you actually can do it like this in Craft 3 (rather than reloading the entry): twitter.com/nystudio107/status/1023803776279498752 – andrew.welch Aug 14 '18 at 14:09
  • @andrew.welch Aha, nice one! Didn't know about that – I'll edit the answer. Thanks! – Mats Mikkel Rummelhoff Aug 14 '18 at 15:45
  • Helpful explanation Mats. I have already been using the Debug Toolbar. What about the transforms, can/how/should they be pre-loaded? – Paul Frost Aug 14 '18 at 17:15
  • Eager Beaver really is just a shorthand way to do exactly that @MatsMikkelRummelhoff -- while there is no great need for the plugin on Craft 3, it makes the syntax a little cleaner, and ends up calling the same Craft core routine. The primary reason I ported it to Craft 3 is so people who used it on Craft 2.x could upgrade their sites without changing their template code. – andrew.welch Aug 14 '18 at 20:35
  • @MatsMikkelRummelhoff is a machine. – Brad Bell Aug 14 '18 at 22:49

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.