7

Is there any way to "eagerly load" Craft element relations a la Yii's ActiveRelation "with" function?

http://www.yiiframework.com/doc/api/1.1/CActiveRecord#with-detail

This seems like a very handy way to request one big query instead of many small ones in cases where you know up front that you are going to be using all the relations.

** UPDATE **

Okay after experimenting with this, I guess my real question should have been, is there any way to do this kind of thing at the ElementCriteriaModel level, taking advantage of Craft relational fields.

For instance, I want to get a list of children of a Structure entry, along with all the assets in a field:

foreach ($entry->children->find() as $child) {
    // do something with $child->assetsField...
}

Is there any way to load those assets in one big query rather than individually for each child?

2
  • Which “relations” are you referring to? Active record relations (e.g. what the with() method is for), or Craft element relations? Oct 16, 2014 at 20:21
  • Sorry, I should have made it clear. I'm talking about Craft element relations. Edited the question to clarify. Oct 17, 2014 at 17:19

3 Answers 3

7

This feature has now made it into Craft core as of version 2.6.

See this link for documentation on the subject:
https://craftcms.com/docs/templating/eager-loading-elements

6

I've been thinking about ways to achieve this recently, and I've put together a basic proof-of-concept plugin for fetching one level of relations for a collection of elements, with no more than two extra queries for each element type.

One query to get the IDs of the related elements, and then another to actually fetch and return the elements with the ElementsService.

The plugin then matches those related elements back to the entries through a dynamically-added fetched() method.

Say you want to fetch all the Assets related through the mainImage and additionalImages fields, over a collection of product entries:

{% set entries = craft.entries.section('products').find %}
{% do craft.fetch.assets(entries, 'mainImage, additionalImages') %}

{% for entry in entries %}
    {% if entry.fetched('mainImage')|length %}
        <img src="{{ (entry.fetched('mainImage')|first).getUrl('large') }}"> 
    {% endif %}

    {% for image in entry.fetched('additionalImages') %}
        <img src="{{ image.getUrl('medium') }}"> 
    {% endfor %}
{% endfor %}

I wouldn't say it's necessarily production-ready yet, but I've thrown it up on Github - https://github.com/mdcpepper/fetch

2
  • Very cool Mike! I think something like this is definitely on the right track.
    – Peter Tell
    Mar 4, 2015 at 16:16
  • As noted in another answer, this functionality is now in Craft core, as of version 2.6.
    – Simon East
    Aug 5, 2016 at 2:44
3

Sure thing.

$groupRecord = FieldGroupRecord::model()->with('fields')->findById($groupId);

That will eager load the related data from the "fields" table with the "fieldgroups" table.

4
  • Oh duh, I don't know how I missed that Craft Records inherit that. I'll experiment. Thanks! Oct 17, 2014 at 17:20
  • After some experimentation, I've updated the question to better reflect what I should have asked originally. Oct 19, 2014 at 14:44
  • Or, if there is an EntryRecord-based way to eagerly load relations from a Craft field, I'll happily take that :) e.g. the equivalent of this: $entry->children->with('assetsField')->find() Oct 19, 2014 at 14:53
  • I'm actually curious about this too as it relates to grabbing entries and pulling in their associated relationships. i.e., for an entry that has "x" associated images and "x" associated entries, eager load all those relationships in one (or two) DB calls
    – Peter Tell
    Dec 15, 2014 at 22:03

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.