How can I use break or continue within for loop in Twig template?
This can be nearly done by setting a new variable as a flag to break
iterating:
{% set break = false %}{% for post in posts if not break %} <h2>{{ post.heading }}</h2> {% if post.id == 10 %} {% set break = true %} {% endif %}{% endfor %}
An uglier, but working example for continue
:
{% set continue = false %}{% for post in posts %} {% if post.id == 10 %} {% set continue = true %} {% endif %} {% if not continue %} <h2>{{ post.heading }}</h2> {% endif %} {% if continue %} {% set continue = false %} {% endif %}{% endfor %}
But there is no performance profit, only similar behaviour to the built-in
break
andcontinue
statements like in flat PHP.
From docs TWIG 2.x docs:
Unlike in PHP, it's not possible to break or continue in a loop.
But still:
You can however filter the sequence during iteration which allows you to skip items.
Example 1 (for huge lists you can filter posts using slice, slice(start, length)
):
{% for post in posts|slice(0,10) %} <h2>{{ post.heading }}</h2>{% endfor %}
Example 2 works TWIG 3.0 as well:
{% for post in posts if post.id < 10 %} <h2>{{ post.heading }}</h2>{% endfor %}
You can even use own TWIG filters for more complexed conditions, like:
{% for post in posts|onlySuperPosts %} <h2>{{ post.heading }}</h2>{% endfor %}
A way to be able to use {% break %}
or {% continue %}
is to write TokenParser
s for them.
I did it for the {% break %}
token in the code below. You can, without much modifications, do the same thing for the {% continue %}
.
AppBundle\Twig\AppExtension.php:
namespace AppBundle\Twig;class AppExtension extends \Twig_Extension{ function getTokenParsers() { return array( new BreakToken(), ); } public function getName() { return 'app_extension'; }}
AppBundle\Twig\BreakToken.php:
namespace AppBundle\Twig;class BreakToken extends \Twig_TokenParser{ public function parse(\Twig_Token $token) { $stream = $this->parser->getStream(); $stream->expect(\Twig_Token::BLOCK_END_TYPE); // Trick to check if we are currently in a loop. $currentForLoop = 0; for ($i = 1; true; $i++) { try { // if we look before the beginning of the stream // the stream will throw a \Twig_Error_Syntax $token = $stream->look(-$i); } catch (\Twig_Error_Syntax $e) { break; } if ($token->test(\Twig_Token::NAME_TYPE, 'for')) { $currentForLoop++; } else if ($token->test(\Twig_Token::NAME_TYPE, 'endfor')) { $currentForLoop--; } } if ($currentForLoop < 1) { throw new \Twig_Error_Syntax( 'Break tag is only allowed in \'for\' loops.', $stream->getCurrent()->getLine(), $stream->getSourceContext()->getName() ); } return new BreakNode(); } public function getTag() { return 'break'; }}
AppBundle\Twig\BreakNode.php:
namespace AppBundle\Twig;class BreakNode extends \Twig_Node{ public function compile(\Twig_Compiler $compiler) { $compiler ->write("break;\n") ; }}
Then you can simply use {% break %}
to get out of loops like this:
{% for post in posts %} {% if post.id == 10 %} {% break %} {% endif %} <h2>{{ post.heading }}</h2>{% endfor %}
To go even further, you may write token parsers for {% continue X %}
and {% break X %}
(where X is an integer >= 1) to get out/continue multiple loops like in PHP.