在 Twig 模板中,如何在 for 循环中使用 break 或 Continowinfor?

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

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

如何在 Twig 中使用 PHP 控制结构的 breakcontinue的行为?

141130 次浏览

来自 Docs TWIG 2. x 医生:

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

但是:

然而,您可以在迭代过程中过滤序列,这允许您跳过项目。

示例1(对于大型列表,您可以使用 sliceslice(start, length)过滤文章) :

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

示例2同样适用于 TWIG 3.0:

{% 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 %}

但是有 没有的性能利润,只有类似的行为内置的 breakcontinue语句,如在扁平 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")
    ;
    }
    }
    

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 %}

更进一步,您可以将 {% continue X %}{% break X %}(其中 X 是一个整数 > = 1)的令牌解析器写入 取出/继续 PHP 中的多个循环

From @NHG comment — works perfectly

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

我已经找到了一个很好的解决方案(喜欢上面的休息样本)。 在这里我不想列出“代理”。在 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 %}