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

42

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

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

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

Теги:
for-loop
twig
break

5 ответов

61
Лучший ответ

Это может быть почти, если установить новую переменную в качестве флага для 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.

  • 1
    Это полезно. В моем случае мне просто нужно показать / получить первый результат. Есть ли способ в Twig, чтобы получить только первое значение? Это только для лучшей производительности.
  • 1
    @pathros Чтобы получить первое значение, используйте first фильтр веток : twig.sensiolabs.org/doc/filters/first.html
Показать ещё 3 комментария
72

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

В отличие от 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 %}
  • 27
    Более того, если вы хотите добиться прерывания цикла после 10 итераций, вы можете использовать что-то вроде этого: {% for post in posts|slice(0,10) %}
  • 4
    Хорошо, спасибо, я, вероятно, пропустил. Unlike in PHP, it's not possible to break or continue in a loop. когда читаешь документы. Но я думаю, что break и continue это хорошие функции, которые нужно добавить
Показать ещё 8 комментариев
4

Способ использования {% 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.

  • 0
    Это имеет смысл, спасибо!
  • 8
    Это просто перебор. Петли ветки должны поддерживать разрывы и продолжаться изначально.
Показать ещё 1 комментарий
3

Я нашел хорошую работу для продолжения (см. выше пример опроса). Здесь я не хочу перечислить "агентство". В 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 %}
2

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

{% for post in posts|slice(0,10) %}
  • 0
    Работает отлично, спасибо!
  • 0
    @ Базит, если посты упорядочены по дате?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню