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.