A plugin for basic eager loading of relationships in Craft.
NOTE: This is experimental, hasn't been run in production anywhere, and the actual performance gain you might get depends on a few other factors too so YMMV. Please feel free to fork and improve!
- Clone or download this repository and extract to a
fetch
directory in your Craft plugins directory. - Install the plugin through the Craft control panel.
To iterate over a collection of Entries and output their Assets, you would normally use something like the following in your template:
{% set entries = craft.entries.section('portfolio') %}
{% for entry in entries %}
{% for image in entry.images %}
<img src="{{ image.url }}" alt="{{ image.title }}">
{% endfor %}
{% endfor %}
But this code performs n+1 queries - one for the craft.entries
call, and then another for each
call to entry.images
, that fetches the related Assets.
Using this plugin, to fetch all the Assets for one or more fields in one go you
would instead use the craft.fetch.assets
template variable, passing in the Entries you've already
loaded:
{% set entries = craft.entries.section('portfolio').find() %}
{% do craft.fetch.assets(entries, 'images, moreImages') %}
This way only three queries are executed - the one to get the initial Entries, another to fetch the relations' information, and a final one to fetch all of the related Elements themselves.
Now, when you loop over your Entries, each one has a new fetched
method that is used to access
the related Elements. You can pass in one or more field handles to limit the Elements that are
returned, or leave it out to get everything.
{% for entry in entries %}
{% for image in entry.fetched('images, moreImages') %}
<img src="{{ image.url }}" alt="{{ image.title }}">
{% endfor %}
{% endfor %}
There are template variable methods for all the built-in Craft Element Selector Field Types:
craft.fetch.assets
craft.fetch.categories
craft.fetch.entries
craft.fetch.tags
craft.fetch.users
And Fetch works with custom Element Types too:
{% do craft.fetch.elements('YourPlugin_CustomElementType', entries, 'customElementsField') %}
As with the Craft ElementCriteriaModel
s, you can pass multiple field handles as a comma-separated
string or a Twig array.
{% set entries = craft.entries.section('products').find() %}
{% do craft.fetch.assets(entries, 'productImages') %}
<section class="products">
{% for entry in entries %}
<article>
<h1>{{ entry.title }}</h1>
{% if entry.fetched('productImages')|length %}
<ul class="images">
{% for image in entry.fetched('productImages') %}
<li><img src="{{ image.url }}" alt="{{ image.title }}"></li>
{% endfor %}
</ul>
{% endif %}
</article>
{% endfor %}
</section>
If you are primarily looping through the “target” elements in the context of a relationship, and want to eager-load their source elements, you set that up the same way you would otherwise, but prefix your field handle with reverse:
.
{% set categories = craft.categories.group('productCategories').find() %}
{% do craft.fetch.entries(categories, 'reverse:productCategories') %}
<section class="categories">
{% for category in categories %}
<article>
<h1>{{ category.title }}</h1>
{% if category.fetched('reverse:productCategories')|length %}
<ul class="products">
{% for entry in category.fetched('reverse:productCategories') %}
<li>{{ entry.getLink() }}</li>
{% endfor %}
</ul>
{% endfor %}
</article>
{% endfor %}
</section>
- Added support for adding a
reverse:
prefix to field handles, for eager-loading reverse relations.
- Access related Elements from a nicer
fetched
method dynamically added to the source Elements. - Improved documentation and added LICENSE file.
- Limit relations by field handles rather than IDs.
- Fetch related Elements for multiple source elements by field IDs.
- Tests.
- Make sure it works with localized setups.
MIT