How can I use break or continue within for loop in Twig template?

我们两清 提交于 2019-11-26 15:27:26

问题


I try to use a simple loop, in my real code this loop is more complex, and I need to break this iteration like:

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

How can I use behavior of break or continue of PHP control structures in Twig?


回答1:


From docs TWIG docs:

Unlike in PHP, it's not possible to break or continue in a loop.

But still:

You can however filter the sequence during iteration which allows you to skip items.

Example 1 (for huge lists you can filter posts using slice, slice(start, length)):

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

Example 2:

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

You can even use own TWIG filters for more complexed conditions, like:

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



回答2:


This can be nearly done by setting a new variable as a flag to break iterating:

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

An uglier, but working example for 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 %}

But there is no performance profit, only similar behaviour to the built-in break and continue statements like in flat PHP.




回答3:


From @NHG comment — works perfectly

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



回答4:


A way to be able to use {% break %} or {% continue %} is to write TokenParsers for them.

I did it for the {% break %} token in the code below. You can, without much modifications, do the same thing for the {% 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 %}

To go even further, you may write token parsers for {% continue X %} and {% break X %} (where X is an integer >= 1) to get out/continue multiple loops like in PHP.




回答5:


I have found a good work-around for continue (love the break sample above). Here I do not want to list "agency". In PHP I'd "continue" but in twig, I came up with alternative:

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

OR I simply skip it if it doesn't meet my criteria:

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



回答6:


just try like this

  {% for tr in time_reports %}
      {% if conditions %}
        .....
      {% endif %}
  {% endfor %}


来源:https://stackoverflow.com/questions/21672796/how-can-i-use-break-or-continue-within-for-loop-in-twig-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!