如何在Twig模板的for循环中使用break或continue?


97

我尝试使用一个简单的循环,在我的实际代码中,这个循环更加复杂,我需要break像这样的迭代:

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

我如何使用的行为,breakcontinue在枝杈PHP控制结构?

Answers:


126

通过将新变量设置为迭代标志,几乎可以完成此操作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 %}

但是没有性能收益,只有与内置PHP breakcontinue类似内部PHP语句类似的行为。


1
它是有益的。就我而言,我只需要显示/获得第一个结果。Twig中是否有办法只获得第一个价值?这仅出于更好的性能目的。
Pathros '02

1
@pathros为了获得第一个值,请使用first树枝过滤器:twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky 2016年

1
爱笔记。一直在尝试我的最后10分钟,发现实际上没有帮助的事:D
Tree Nguyen

2
值得注意的是,这不会破坏代码的执行,set break = true除非您将其放在else语句中,否则下面的任何内容都将被执行。参见twigfiddle.com/euio5w
Gus

2
@Gus Yep,这就是为什么我要set break = true最后加上if语句。但是,是的,这取决于您的代码,因此感谢您对其进行澄清
Victor Bocharsky

121

从文档TWIG 文档

与PHP不同,它不可能中断或继续循环。

但仍然:

但是,您可以在迭代过程中过滤序列,从而可以跳过项目。

示例1(对于庞大的列表,您可以使用slice,过滤帖子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 2014年

5
好的,谢谢,我可能Unlike in PHP, it's not possible to break or continue in a loop.在阅读文档时错过了。但是我认为break并且continue是一个很好的功能,需要添加
Victor Bocharsky 2014年

您无法在loop语句中访问循环变量!
Maximus

不起作用。长长的清单,for第一次击中后应该是易碎的。@VictorBocharsky的答案是正确的
Vasilii Suricov '19

您可以使用@VasiliiSuricov {% for post in posts|slice(0,10) %}获取大量列表。看到我的第一条评论。我也更新了答案。
NHG

12

一种可以使用{% 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中)


10
那太过分了。Twig循环应支持中断并在本地继续。
手工艺者

如果您不想/不能使用过滤器,那就很好。
丹尼尔·德赫斯特

squirrelphp/twig-php-syntax提供{% break %}{% break n %}以及{% continue %}令牌。
mts knn

@mtsknn和作者使用并改进了我为此答案编写的代码!
Jules Lamur

@JulesLamur,您说的是“ @mtsknn和作者”,但是我不参与该库。
mts knn


6

我找到了一个继续的好方法(喜欢上面的break示例)。在这里,我不想列出“代理商”。在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 %}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.