Comment puis-je utiliser pause ou continuer dans la boucle for dans le modèle Twig?


97

J'essaye d'utiliser une boucle simple, dans mon vrai code cette boucle est plus complexe, et j'ai besoin de breakcette itération comme:

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

Comment puis-je utiliser le comportement de breakou continuedes structures de contrôle PHP dans Twig?

Réponses:


125

Cela peut être presque fait en définissant une nouvelle variable comme indicateur d' breakitération:

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

Un exemple plus laid, mais fonctionnel 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 n'y a pas de gain de performance, seulement un comportement similaire à celui des instructions intégrées breaket continuecomme en PHP plat.


1
C'est utile. Dans mon cas, j'ai juste besoin de montrer / obtenir le premier résultat. Y a-t-il un moyen dans Twig d'obtenir juste la première valeur? Ceci est uniquement pour de meilleures performances.
Pathros

1
@pathros Pour obtenir la première valeur, utilisez le firstfiltre Twig
Victor Bocharsky

1
J'adore la note. J'ai essayé mes 10 dernières minutes pour trouver quelque chose qui n'est pas vraiment utile: D
Tree Nguyen

2
Il est à noter que cela n'interrompra pas l'exécution du code, tout ce qui se trouve ci set break = true- dessous sera exécuté à moins que vous ne le mettiez dans une elseinstruction. Voir twigfiddle.com/euio5w
Gus

2
@Gus Yep, c'est pourquoi je voulais mettre cette déclaration si set break = trueà la toute fin . Mais oui, cela dépend de votre code, alors merci de l'avoir mentionné pour clarifier
Victor Bocharsky

120

À partir de docs TWIG docs :

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

Mais reste:

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

Exemple 1 (pour les listes volumineuses, 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 vos propres filtres TWIG pour des conditions plus complexes, comme:

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

28
De plus, si vous voulez obtenir une boucle de rupture après 10 itérations, vous pouvez utiliser qc comme ça:{% for post in posts|slice(0,10) %}
NHG

5
OK, merci, j'ai probablement raté la Unlike in PHP, it's not possible to break or continue in a loop.lecture de la documentation. Mais je pense breaket continuec'est une bonne fonctionnalité, qu'il faudrait ajouter
Victor Bocharsky

Vous ne pouvez pas accéder à la variable de boucle dans l'instruction de boucle!
Maximus

ne fonctionne pas. longue liste, fordevrait être cassable après le premier coup. La réponse de @VictorBocharsky est juste
Vasilii Suricov

@VasiliiSuricov vous pouvez utiliser {% for post in posts|slice(0,10) %}pour de grandes listes. Voir mon premier commentaire. J'ai également mis à jour ma réponse.
NHG

12

Une façon de pouvoir utiliser {% break %}ou {% continue %}est d'écrire des 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 de 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) pour sortir / continuer plusieurs boucles comme en PHP .


10
C'est juste exagéré. Les boucles Twig doivent supporter les pauses et se poursuivent nativement.
crafter

C'est bien si vous ne voulez pas / ne pouvez pas utiliser de filtres.
Daniel Dewhurst

La squirrelphp/twig-php-syntaxbibliothèque fournit {% break %}, {% break n %}et des {% continue %}jetons.
mts knn

@mtsknn et les auteurs ont utilisé et amélioré le code que j'ai écrit pour cette réponse!
Jules Lamur

@JulesLamur, vous avez dit «@mtsknn et les auteurs», mais je ne suis pas impliqué dans cette bibliothèque.
mts knn

9

De @NHG comment - fonctionne parfaitement

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

@Basit si les articles sont classés par date?
Vasilii Suricov

6

J'ai trouvé une bonne solution pour continuer (j'adore l'exemple de pause ci-dessus). Ici, je ne veux pas lister "agence". En PHP, je "continuerais" mais en brindille, j'ai trouvé 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 l'ignore simplement s'il ne répond pas à mes critères:

{% for tr in time_reports %}
    {% if not tr.isApproved %}
        .....
    {% endif %}
{% endfor %}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.