Як я можу використовувати перерву або продовжити цикл for у шаблоні Twig?


97

Я намагаюся використовувати простий цикл, у моєму реальному коді цей цикл є більш складним, і мені потрібна breakтака ітерація, як:

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

Як я можу використовувати поведінку структур управління PHP breakабо continueв PHP?

Відповіді:


125

Це майже можна зробити, встановивши нову змінну як прапор для 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
Віктор Бочарський

1
Люблю нотатку. Я намагався мої останні 10 хвилин знайти щось, що не дуже корисно: D
Дерево Нгуєн

2
Варто зазначити, що це не призведе до порушення коду, все, що нижче, set break = trueбуде виконано, якщо ви не вкажете це у elseзаяві. Дивіться twigfiddle.com/euio5w
Гас

2
@Gus Так, ось чому я мав намір викласти це твердження if set break = trueу самому кінці . Але так, це залежить від вашого коду, тож дякую за згадку про це
Віктор Бочарський

120

Від документи TWIG документи :

На відміну від PHP, неможливо зламати або продовжити цикл.

Але все ж:

Однак ви можете фільтрувати послідовність під час ітерації, що дозволяє пропускати елементи.

Приклад 1 (для величезних списків ви можете фільтрувати повідомлення за допомогою фрагмента , slice(start, length)):

{% for post in posts|slice(0,10) %}
    <h2>{{ post.heading }}</h2>
{% endfor %}

Приклад 2:

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

Ви навіть можете використовувати власні фільтри TWIG для більш складних умов, таких як:

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

28
Більше того, якщо ви хочете отримати цикл розриву після 10 ітерацій, ви можете використовувати sth так:{% for post in posts|slice(0,10) %}
NHG

5
Добре, дякую, я, мабуть, пропустив, Unlike in PHP, it's not possible to break or continue in a loop.коли читав документи. Але я думаю breakі continueє хороші особливості, які потрібно було б додати
Віктор Бочарський

Ви не можете отримати доступ до змінної циклу в операторі циклу!
Максим

не працює. довгий список, його forслід поламати після першого попадання. Відповідь @VictorBocharsky правильна
Василій Суріков,

@VasiliiSuricov ви можете використовувати {% for post in posts|slice(0,10) %}для величезних списків. Дивіться мій перший коментар. Я також оновив свою відповідь.
NHG

12

Спосіб, яким можна скористатися {% break %}або {% continue %}написати TokenParserдля них s.

Я зробив це для {% 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 .


10
Це просто надмірне. Петлі гілочки повинні підтримувати розриви і продовжуватись споконвічно.
крафтер

Це приємно, якщо ви не хочете / не можете використовувати фільтри.
Деніел Дьюхерст,

squirrelphp/twig-php-syntaxБібліотека надає {% break %}, {% break n %}і {% continue %}жетони.
mts knn

@mtsknn та автори використали та вдосконалили код, який я написав для цієї відповіді!
Жуль Ламур,

@JulesLamur, ти сказав "@mtsknn та автори", але я не пов'язаний з цією бібліотекою.
mts knn


6

Я знайшов хорошу практику для продовження (обожню зразок перерви вище). Тут я не хочу перераховувати "агентство". У PHP я б "продовжив", але в "гілочці" я придумав альтернативу:

{% 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 %}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.