It looks like Twig loop variables reflect the total count of results regardless of the limit parameter. For instance in the following the loop.length tag outputs 8 for me instead of 1. Only with chained parameters and get() does it output correctly.
{% set related = craft.entries({
section: 'blog',
limit: 1
}) %}
{% for post in related %}
{{ loop.length }}
{% endfor %}
Issue here is that Twig sets loop.length to the count of the sequence being iterated over…
$length = count($context['_seq']);
// ...
$context['loop']['length'] = $length;
…which in this case is a craft\elements\db\ElementQuery object that implements PHP’s Countable interface, however its count() method is actually provided by its base class, yii\db\Query, which does not care about Countable, and has a different idea of what that method should do (return the total number of rows that match the query, ignoring the limit).
Which puts us at a bit of a crossroads. If we fix this bug by overriding count() so it matches Countable’s definition of the method, we are changing the behavior of the function from what its base class intended it to be.
Not totally sure what the right answer is yet; will have to think this over.
@leigeber Just occurred to me that there’s a quick workaround you can use for this, while we sort it out: Just explicitly call .all() in the for loop:
{% for post in related.all() %}
That way Twig is iterating over an actual array, rather than the ElementQuery object.
Perfect, was just about to ask how to cast without chaining. Thanks, works great for now.
After talking this over internally and with one of the Yii core devs, we’ve determined that there’s no good way to resolve this, besides to stop letting element queries be iterated over directly.
Considering most people get confused about when to use/not use .find() in Craft 2, and we expect the same will happen with .all() in Craft 3, just forcing everyone to always type .all() actually feels like an improvement, even if a little more verbose.
So as of 73c319ecbb376481b9e00da6aa4528d4d8ce9613, looping through element queries directly is now deprecated. It will still work for the time being (albeit with the loop.length issue you ran into if the limit param is set), but we will be removing support for it altogether in Craft 4.
Most helpful comment
After talking this over internally and with one of the Yii core devs, we’ve determined that there’s no good way to resolve this, besides to stop letting element queries be iterated over directly.
Considering most people get confused about when to use/not use
.find()in Craft 2, and we expect the same will happen with.all()in Craft 3, just forcing everyone to always type.all()actually feels like an improvement, even if a little more verbose.So as of 73c319ecbb376481b9e00da6aa4528d4d8ce9613, looping through element queries directly is now deprecated. It will still work for the time being (albeit with the
loop.lengthissue you ran into if thelimitparam is set), but we will be removing support for it altogether in Craft 4.