Hi,
is it possible to break a loop?
I googled and searched it here, but it seems like this isn't supported in Twig.
So I was wandering - how come it's left out?
It is not possible to break a for loop in twig, and it is explicitly documented
http://twig.sensiolabs.org/doc/tags/for.html#adding-a-condition
"Using the loop variable within the condition is not recommended as it will probably not be doing what you expect it to. For instance, adding a condition like loop.index > 4 won't work as the index is only incremented when the condition is true (so the condition will never match)."
Yeah that's true, it's documented. But in my mind it's a real integrator/dev need to be able to break a loop depending on the loop index and not just on one of the current object property/method.
Please don't say : "Just create a method which do the job in your looped object".
If PHP has its own break instruction that's because we need it. We need it too in twig with a {% break %} tag or the loop.index condition in the loop. Give me a good reason to explain why twig don't have this instruction. The example given in the documentation is a false reason.
Thank you.
@Maxooo loop.index gives you the index of the iterated items, so it is not incremented when the condition evalutaes to false. This is why you cannot rely on it in the condition
how dum is that ... a break should be included
who cares about the loop.index
{% set my_array = ['a', 'b', 'c', 'b'] %}
{% set counter = 0 %}
{% for element in my_array %}
{% if element == 'b' %}
{% set index = counter %}
{% break %}
{% endif %}
{% set counter = counter + 1 %}
{% endfor %}
{% set counter = 0 %}
{% set found = false %}
{% set my_array = ['a', 'b', 'c', 'b'] %}
{% for element in my_array %}
{% if element == 'b' %}
{% if not found %}
{% set index = counter %}
{% set found = true %}
{% endif %}
{% endif %}
{% set counter = counter + 1 %}
{% endfor %}
Holly
+1 for thinking that a break command in twig would be useful. It's probably rarely _needed_ (as in you can live without it) but code would be cleaner if we had a break available and I don't think it would hurt.
@stof Thanks for you reply. Yeah I know and I understand the reason why we can't rely on the loop.index I just took this instruction as a workaround to the break absence.
Give me a good reason to explain why twig don't have this instruction. The example given in the documentation is a false reason.
@Maxooo by adding a break clause you would break (no pun intended) the purpose of the separation of concerns. Twig is not designed to do any business logic, and it should by done (IMHO) in your controllers, not in your views.
I'm -1'ing that PR, it would ultimately lead to bad, inconsistent code.
A break isn't necessarily needed for business logic. IMHO it's needed to efficiently render the view using the arrays/collections passed through to it. I understand where you guys are coming from but would it really hurt to have break / continue exposed through twig? You don't _have_ to use them, but many people would find it helpful (and not everybody has a need for such strict separation of concerns).
With continue/break (or workarounds) you can, for example, pass one collection and re-use it in the view in several places. Without these you need to prep and pass a different collection for each display section which at best takes some getting used to and at worse can lead to harder to read code when people don't realise this and start hacking around in twig.
(It's also _really_ odd to work with loops in twig ... it feels really strange to have no continue or break functionality!)
Thank you @caponica, I could not say it better myself. Is break clause really inconsistent ? In my mind, workarounds and hacks reproducing the simple break processing are inconsistent (look at the @ernestob one ).
I just had to do something similar and I think it could be interesting for the others.
If we take the case where u should stop looping after X, u can do it so:
{% set cnt = 10 }
{% for el in collection if cnt > 0 %}
{% set cnt = cnt - 1 %}
{# do something #}
{% endfor %}
Maybe this will help someone :)
Breaking a loop is not business logic at all, it is display logic that belongs in twig at the view layer. How many items in a specific array do I want to display on this page? UPDATE: the way to accomplish this is using the Twig filter "slice" to cut down your array in the template .
7 years later, the need is still here... 🙈
still in need and not implemented
I see one craft3 implementation for twig, can it be extended/implemented.
https://github.com/marionnewlevant/craft-mnbreakandcontinue
{% for x in allx %}
//statements
//conditions
{% set break = true %}
{% endfor %}
It's amazing that years later and still nothing has been done. Just incredible. If "break" is business logic then "for and if" statements are business logic as well. SMH.
What’s incredible is that nobody took the time to submit a PR in 7 years. Closing now. If someone wants to work on it, feel free too open a PR.
Hi Folks,
you can achieve a break in a for loop like following:
# /src/Twig/Extension/FunctionExtension.php
<?php
namespace YourApp\Twig\Extension;
class FunctionExtension extends \Twig\Extension\AbstractExtension
{
function getTokenParsers() {
return array(
new BreakToken(),
);
}
public function getName()
{
return FunctionExtension::t('Vapita For-Loop-Break Tag');
}
}
# /src/Twig/Extension/BreakToken.php
<?php
namespace YourApp\Twig\Extension;
use Twig\Error\SyntaxError;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;
class BreakToken extends AbstractTokenParser
{
public function parse(Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$stream->expect(Token::BLOCK_END_TYPE);
return new BreakNode(array(), array(), $lineno, $this->getTag());
}
public function getTag()
{
return 'break';
}
}
# /src/Twig/Extension/BreakNode.php
<?php
namespace YourApp\Twig\Extension;
use Twig\Compiler;
use Twig\Node\Node;
class BreakNode extends Node
{
public function compile(Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler->write("break;\n");
}
}
Then you can use the {% break %}-tag in your loop.
{% for x in y %}
{% if x is true %}
{% break %}
{% endif %}
{% endfor %}
p.s.: it does not check whether the break tag is inside a for-loop or not.
kind regards,
Ben
you might be better off using my PR as it deals with more case, just not all possible ones... maybe someone wants to pick it up?
Most helpful comment
7 years later, the need is still here... 🙈