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

前端 未结 5 1630
眼角桃花
眼角桃花 2020-12-01 00:35

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


        
相关标签:
5条回答
  • 2020-12-01 01:06

    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.

    0 讨论(0)
  • 2020-12-01 01:10

    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 %}
    
    0 讨论(0)
  • 2020-12-01 01:13

    From @NHG comment — works perfectly

    {% for post in posts|slice(0,10) %}
    
    0 讨论(0)
  • 2020-12-01 01:20

    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.

    0 讨论(0)
  • 2020-12-01 01:22

    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 %}
    
    0 讨论(0)
提交回复
热议问题