2

I am trying to get to grips with Eager Loading. I have the following code:

{% for article in entry.relatedArticles %}
<div class="c-card // l-card--third">
    <a href="{{ article.url }}">
        <img src="{{ article.bannerImage.first().getUrl('cardImage') }}" alt="{{ article.title }}" class="c-card__img">
        <div class="c-card__bar // js_fitcontent">
            {% set array = article.title|split(' ', 2) %}
            <p class="c-card__title"><strong>{{ attribute(array, 0) }}</strong> {{ attribute(array, 1) }}</p>
            <p class="c-card__msg // js-fittext">Posted on {{ helpers.date_formatted(entry.postDate) }}</p>
        </div>

        <div class="c-card__over">
            <div class="c-card__over-content">
                <div class="animate">
                    <p class="c-card__title"><strong>Related</strong> Article</p>
                    <p class="c-card__msg  // half-margin-bottom">
                        <strong>{{ article.title }}</strong><br />
                        <time datetime="{{ entry.postDate|date('Y-m-d H:i:s') }}" class="date">
                            <svg role="img" class="icon // icon-date" title="Date Icon">
                                <use xlink:href="/assets/img/icons.svg#icon-date"></use>
                            </svg>{{ helpers.date_formatted(entry.postDate) }}
                        </time>
                    </p>
                    <p class="btn // btn--sub">Read Article</p>
                </div>
            </div>
        </div>
    </a>
</div>
{% endfor %}

Looking on the console this combined with the other page requests totals 76 requests. I thought I perhaps should add some eager loading 9at least tot he image transform) So I tried the following:

{% for article in entry.relatedArticles.withTransforms('cardImage') %}
<div class="c-card // l-card--third">
    <a href="{{ article.url }}">
        <img src="{{ article.bannerImage.first().getUrl('cardImage') }}" alt="{{ article.title }}" class="c-card__img">
        <div class="c-card__bar // js_fitcontent">
            {% set array = article.title|split(' ', 2) %}
            <p class="c-card__title"><strong>{{ attribute(array, 0) }}</strong> {{ attribute(array, 1) }}</p>
            <p class="c-card__msg // js-fittext">Posted on {{ helpers.date_formatted(entry.postDate) }}</p>
        </div>

        <div class="c-card__over">
            <div class="c-card__over-content">
                <div class="animate">
                    <p class="c-card__title"><strong>Related</strong> Article</p>
                    <p class="c-card__msg  // half-margin-bottom">
                        <strong>{{ article.title }}</strong><br />
                        <time datetime="{{ entry.postDate|date('Y-m-d H:i:s') }}" class="date">
                            <svg role="img" class="icon // icon-date" title="Date Icon">
                                <use xlink:href="/assets/img/icons.svg#icon-date"></use>
                            </svg>{{ helpers.date_formatted(entry.postDate) }}
                        </time>
                    </p>
                    <p class="btn // btn--sub">Read Article</p>
                </div>
            </div>
        </div>
    </a>
</div>
{% endfor %}

However, this also still results in 76 requests.

So my question is this; Have I misunderstood when I need to use Eager Loading? or is my code incorrect and thus eager loading isn't actually taking place?

Thanks.

2 Answers 2

4

Your syntax is a bit off.

The withTransforms parameter is an Asset criteria, meaning it is only relevant when you're pulling Assets. Appending it to entry.relatedArticles (an Entries field, returning an ElementCriteriaModel) like you're currently attempting does nothing, but this does:

{% set assets = entry.someAssetsField.find({
    withTransforms: ['someTransform', 'someOtherTransform']
}) %}

This means your current code should probably be refactored to look something like this (simplified):

{% for article in entry.relatedArticles %}
    ...
    {% set bannerImage = article.bannerImage.find({
        withTransforms: ['cardImage']
    })[0] ?? null %}
    {% if bannerImage %}
        <img src="{{ bannerImage.getUrl('cardImage') }}" />
    {% endif %}
    ...
{% endfor %}

Assuming there's not a ton of relatedArticles entries, and considering the fact that there's only one image per entry, the above might not make a huge impact on your performance. To take things a step further, you might consider eager loading the actual Asset field(s) as well as their transforms. There's a couple of ways to do this – you could use the same approach as above, adding the eager loading criteria to the Entries field relatedArticles:

{% set relatedArticles = entry.relatedArticles.find({
  with: [
      ['bannerImage', {
          withTransforms: ['cardImage']
      }]
  ]
}) %}
{% for article in relatedArticles %}
    ...
    {% set bannerImage = article.bannerImage[0] ?? null %}
    {% if bannerImage %}
        <img src="{{ bannerImage.getUrl('cardImage') }}" />
    {% endif %}
{% endfor %}

...or you could use a more "traditional" eager loading approach, using craft.entries and the relatedTo parameter:

{% for article in craft.entries({
    relatedTo: {
      sourceElement: entry,
      field: 'relatedArticles'
    },
    with: [
        ['bannerImage', {
            withTransforms: ['cardImage']
        }]
    ]
}) %}
    ...
    {% set bannerImage = article.bannerImage[0] ?? null %}
    {% if bannerImage %}
        <img src="{{ bannerImage.getUrl('cardImage') }}" />
    {% endif %}
    ...
{% endfor %}

With the above, assuming there's a fair amount of entries involved you'll probably see a decrease in the number of queries executed.

If you want to learn more about eager loading, I recommend taking a look at the official docs on the subject.

9
  • Thanks for this Mats. Is there a general guide as to when you should really use Eager loading? I am not seeing much improvement. Perhaps my implementation isn't 100%. I currently have 77 queries for the whole page. This includes a matrix block, 4 related product, 3 related articles (cards), and three related articles (titles and dates). Commented Nov 9, 2016 at 15:48
  • The general rule is "whenever you need to avoid the N+1 query problem". 77 queries isn't actually a huge number and eager loading isn't a silver bullet, but it can be very effective. Without seeing your complete template its impossible to say if eager loading will make a big difference in your particular case, but if the number of queries doesn't go down at all its likely that your implementation is still off. Commented Nov 9, 2016 at 19:19
  • For more info, see the official docs or Straight Up Craft. There's probably more resources out there, and you could also tune into the official Slack channel to discuss the subject further and/or get more help. Commented Nov 9, 2016 at 19:21
  • Thanks. I will have another look through my code as to why this wasn't working correctly. I tried on Slack but didn't get much input. The first suggestion in your answer I was getting errors, the second more comprehensive approach, worked (no errors) but I can't recall how effective it was. I am only calling three related cards. So might not impact really. I will take another look. Thanks. Commented Nov 9, 2016 at 22:18
  • Let's say you have a template with 4 separate queries (e.g. craft.entries or craft.categories etc), where each query returns only a small number of elements (e.g. 5 entries, 3 categories) with relational data; you're not going to see a huge improvement with eager loading. Eager loading is really only very effective when a single query results in large number of additional queries (e.g. a query that returns a set of 100 entries, each with 3 different relational fields – that would amount to 301 queries in total using lazy loading, but could be reduced to 1 query with eager loading). Commented Nov 9, 2016 at 22:50
3

Thanks for this Mats. Is there a general guide as to when you should really use Eager loading? I am not seeing much improvement

The article Speed up your Craft CMS Templates with Eager Loading I wrote hopefully gives you the "general guide" you asked 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.