web-dev-qa-db-fra.com

Comment utiliser break ou continuer dans la boucle for Twig?

J'essaie d'utiliser une boucle simple, dans mon code réel, cette boucle est plus complexe et j'ai besoin de break cette itération comme:

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

Comment utiliser le comportement de break ou continue de PHP) structures de contrôle dans Twig?

80
Victor Bocharsky

Cela peut être presque fait en définissant une nouvelle variable comme un indicateur à break itérer:

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

Plus moche, mais exemple de travail pour 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 %}

Mais il y a non bénéfice sur les performances, seul un comportement similaire aux instructions break et continue intégrées, comme dans PHP à plat.

101
Victor Bocharsky

De docs TWIG docs :

Contrairement à PHP, il n'est pas possible de casser ou de continuer en boucle.

Mais reste:

Vous pouvez toutefois filtrer la séquence lors de l'itération, ce qui vous permet d'ignorer des éléments.

Exemple 1 (pour les listes énormes, vous pouvez filtrer les publications en utilisant slice , slice(start, length)):

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

Exemple 2:

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

Vous pouvez même utiliser votre propre filtres TWIG pour des conditions plus complexes, telles que:

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

Commentaire de @NHG - fonctionne parfaitement

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

Un moyen de pouvoir utiliser {% break %} ou {% continue %} est d'écrire TokenParsers pour eux.

Je l'ai fait pour le {% break %} jeton dans le code ci-dessous. Vous pouvez, sans trop de modifications, faire la même chose pour le {% 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")
            ;
        }
    }
    

Ensuite, vous pouvez simplement utiliser {% break %} pour sortir des boucles comme celle-ci:

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

Pour aller encore plus loin, vous pouvez écrire des analyseurs de jetons pour {% continue X %} et {% break X %} (où X est un entier> = 1) à sortir/continuer plusieurs boucles comme en PHP .

7
Jules Lamur

J'ai trouvé un bon moyen de contourner (continuez avec l'exemple de pause ci-dessus). Ici, je ne veux pas énumérer "agence". Dans PHP je "continuerais", mais dans twig, j'ai proposé une alternative:

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

OU je le saute simplement s'il ne répond pas à mes critères:

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