Как я могу использовать break или продолжить цикл for в шаблоне Twig?

Я пытаюсь использовать простой цикл, в моем реальном коде этот цикл более сложный, и мне нужно break эту итерацию следующим образом:

 {% for post in posts %} {% if post.id == 10 %} {# break #} {% endif %} <h2>{{ post.heading }}</h2> {% endfor %} 

Как я могу использовать поведение break или continue структуры управления PHP в Twig?

Из документов Документы TWIG:

В отличие от PHP, невозможно разбить или продолжить в цикле.

Но все равно:

Тем не менее, вы можете фильтровать последовательность во время итерации, которая позволяет пропустить элементы.

Пример:

 {% for post in posts if post.id < 10 %} <h2>{{ post.heading }}</h2> {% endfor %} 

Вы можете использовать собственные фильтры TWIG для более сложных условий, например:

 {% for post in posts|onlySuperPosts %} <h2>{{ post.heading }}</h2> {% endfor %} 

Это можно сделать почти путем установки новой переменной в качестве флага для break итерации:

 {% set break = false %} {% for post in posts if not break %} <h2>{{ post.heading }}</h2> {% if post.id == 10 %} {% set break = true %} {% endif %} {% endfor %} 

Более уродливый, но рабочий пример для 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 %} 

Но нет прибыли от производительности, только аналогичное поведение со встроенными break и continue утверждениями, как в плоском PHP.

Способ использования {% break %} или {% continue %} состоит в том, чтобы писать для них TokenParser .

Я сделал это для токена {% break %} в коде ниже. Вы можете без особых изменений сделать то же самое для {% 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") ; } } 

Затем вы можете просто использовать {% break %} чтобы выйти из циклов, например:

 {% for post in posts %} {% if post.id == 10 %} {% break %} {% endif %} <h2>{{ post.heading }}</h2> {% endfor %} 

Чтобы продвинуться дальше, вы можете написать парсеры для токенов для {% continue X %} и {% break X %} (где X – целое число> = 1), чтобы выйти / продолжить несколько циклов, как в PHP .

Я нашел хорошую работу для продолжения (любите образец пробы выше). Здесь я не хочу перечислить «агентство». В PHP я бы «продолжал», но в twig, я придумал альтернативу:

 {% for basename, perms in permsByBasenames %} {% if basename == 'agency' %} {# do nothing #} {% else %} <a class="scrollLink" onclick='scrollToSpot("#{{ basename }}")'>{{ basename }}</a> {% endif %} {% endfor %} 

ИЛИ Я просто пропущу его, если он не соответствует моим критериям:

 {% for tr in time_reports %} {% if not tr.isApproved %} ..... {% endif %} {% endfor %} 

Из комментария @NHG – отлично работает

 {% for post in posts|slice(0,10) %}