Come posso utilizzare break o continuare all'interno del ciclo for nel modello Twig?


97

Provo a usare un ciclo semplice, nel mio codice reale questo ciclo è più complesso e ho bisogno di breakquesta iterazione come:

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

Come posso utilizzare il comportamento delle breako continuedelle strutture di controllo PHP in Twig?

Risposte:


125

Questo può essere fatto quasi impostando una nuova variabile come flag per l' breakiterazione:

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

Un esempio più brutto, ma funzionante per 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 %}

Ma non c'è profitto in termini di prestazioni, solo un comportamento simile al built-in breake alle continueistruzioni come in PHP piatto.


1
È utile. Nel mio caso devo solo mostrare / ottenere il primo risultato. C'è un modo in Twig per ottenere solo il primo valore? Questo è solo per scopi di prestazioni migliori.
Pathros

1
@pathros Per ottenere il primo valore, usa il firstfiltro twig: twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky,

1
Adoro la nota. Ho provato i miei ultimi 10 minuti a trovare qualcosa che non è davvero utile: D
Tree Nguyen,

2
Vale la pena notare che questo non interromperà l'esecuzione del codice, tutto ciò che segue set break = trueverrà eseguito a meno che non lo si inserisca in elseun'istruzione. Vedi twigfiddle.com/euio5w
Gus

2
@ Gus Sì, ecco perché avevo intenzione di mettere quella dichiarazione if set break = truealla fine . Ma sì, dipende dal tuo codice, quindi grazie per averlo menzionato per chiarire
Victor Bocharsky

120

Dai documenti TWIG documenti :

A differenza di PHP, non è possibile interrompere o continuare in un ciclo.

Ma ancora:

È tuttavia possibile filtrare la sequenza durante l'iterazione che consente di saltare gli elementi.

Esempio 1 (per elenchi enormi puoi filtrare i post utilizzando slice , slice(start, length)):

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

Esempio 2:

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

Puoi persino utilizzare i tuoi filtri TWIG per condizioni più complesse, come:

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

28
Inoltre, se vuoi ottenere un break loop dopo 10 iterazioni, potresti usare qc in questo modo:{% for post in posts|slice(0,10) %}
NHG

5
OK, grazie, probabilmente mi sono perso Unlike in PHP, it's not possible to break or continue in a loop.quando ho letto i documenti. Ma penso breakche continuesia una buona caratteristica, che avrebbe bisogno di aggiungere
Victor Bocharsky

Non puoi accedere alla variabile loop nell'istruzione loop!
Massimo

non funziona. lungo elenco, fordovrebbe essere fragile dopo il primo colpo. La risposta di @VictorBocharsky è giusta
Vasilii Suricov

@VasiliiSuricov puoi usare {% for post in posts|slice(0,10) %}per elenchi enormi. Vedi il mio primo commento. Ho anche aggiornato la mia risposta.
NHG

12

Un modo per poter usare {% break %}o {% continue %}è scrivere TokenParserper loro.

L'ho fatto per il {% break %}token nel codice qui sotto. Puoi, senza molte modifiche, fare la stessa cosa per il file {% 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")
            ;
        }
    }

Quindi puoi semplicemente usarlo {% break %}per uscire da loop come questo:

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

Per andare ancora oltre, puoi scrivere parser di token per {% continue X %}e {% break X %}(dove X è un numero intero> = 1) per uscire / continuare più cicli come in PHP .


10
È solo eccessivo. I loop di ramoscello dovrebbero supportare le pause e continuano in modo nativo.
artigiano

Questo è utile se non vuoi / non puoi usare i filtri.
Daniel Dewhurst

La squirrelphp/twig-php-syntaxbiblioteca fornisce {% break %}, {% break n %}e {% continue %}gettoni.
mt KNN

@mtsknn e gli autori hanno utilizzato e migliorato il codice che ho scritto per questa risposta!
Jules Lamur il

@ JulesLamur, hai detto "@mtsknn e gli autori", ma non sono coinvolto in quella libreria.
mts knn

9

Dal commento di @NHG - funziona perfettamente

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

@Basit se i post sono ordinati per data?
Vasilii Suricov

6

Ho trovato una buona soluzione per continuare (adoro il campione di interruzione sopra). Qui non voglio elencare "agenzia". In PHP avrei "continuato" ma in twig, ho trovato un'alternativa:

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

O semplicemente lo salto se non soddisfa i miei criteri:

{% for tr in time_reports %}
    {% if not tr.isApproved %}
        .....
    {% endif %}
{% endfor %}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.