Wie kann ich break verwenden oder innerhalb der for-Schleife in der Twig-Vorlage fortfahren?

96

Ich versuche, eine einfache Schleife zu verwenden. In meinem realen Code ist diese Schleife komplexer und ich muss breakdiese Iteration wie folgt ausführen :

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

Wie kann ich das Verhalten von breakoder continuevon PHP-Kontrollstrukturen in Twig verwenden?

Victor Bocharsky
quelle

Antworten:

123

Dies kann fast erreicht werden, indem eine neue Variable als Flag für die breakIteration gesetzt wird:

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

Ein hässlicheres, aber funktionierendes Beispiel für 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 %}

Es gibt jedoch keinen Leistungsgewinn, nur ein ähnliches Verhalten wie bei den integrierten Anweisungen breakund continueAnweisungen wie bei flachem PHP.

Victor Bocharsky
quelle
1
Es ist nützlich. In meinem Fall muss ich nur das erste Ergebnis zeigen / erhalten. Gibt es in Twig eine Möglichkeit, nur den ersten Wert zu erhalten? Dies dient nur zu besseren Leistungszwecken.
Pathros
1
@ Pathros Um den ersten Wert zu erhalten, verwenden Sie den Zweigfilterfirst : twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky
1
Ich liebe die Notiz. Ich habe meine letzten 10 Minuten versucht, etwas zu finden, das nicht wirklich hilfreich ist: D
Tree Nguyen
2
Es ist erwähnenswert, dass dies nicht die Ausführung von Code brechen, unten etwas set break = truewird , wenn Sie es in einer gestellt werden ausgeführt elseAussage. Siehe twigfiddle.com/euio5w
Gus
2
@Gus Ja, deshalb wollte ich diese if-Anweisung set break = trueganz am Ende setzen . Aber ja, es hängt von Ihrem Code ab, also danke, dass Sie ihn zur Verdeutlichung erwähnt haben
Victor Bocharsky
120

Aus Dokumenten TWIG- Dokumente :

Anders als in PHP ist es nicht möglich, eine Schleife zu unterbrechen oder fortzusetzen.

Aber dennoch:

Sie können die Sequenz jedoch während der Iteration filtern, sodass Sie Elemente überspringen können.

Beispiel 1 (für riesige Listen können Sie Beiträge mit Filterscheibe , slice(start, length)):

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

Beispiel 2:

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

Sie können sogar eigene TWIG-Filter für komplexere Bedingungen verwenden, z.

{% for post in posts|onlySuperPosts %}
    <h2>{{ post.heading }}</h2>
{% endfor %}
NHG
quelle
28
Wenn Sie nach 10 Iterationen eine Unterbrechungsschleife erreichen möchten, können Sie außerdem Folgendes verwenden:{% for post in posts|slice(0,10) %}
NHG
5
OK, danke, ich habe es wahrscheinlich verpasst, Unlike in PHP, it's not possible to break or continue in a loop.als ich die Dokumente gelesen habe. Aber ich denke breakund continueist eine gute Funktion, die
hinzugefügt werden
Sie können nicht auf die Schleifenvariable in der Schleifenanweisung zugreifen!
Maximus
funktioniert nicht lange Liste, forsollte nach dem ersten Treffer zerbrechlich sein. @ VictorBocharskys Antwort ist richtig
Vasilii Suricov
@ VasiliiSuricov können Sie {% for post in posts|slice(0,10) %}für große Listen verwenden. Siehe meinen ersten Kommentar. Ich habe auch meine Antwort aktualisiert.
NHG
11

Eine Möglichkeit , s verwenden {% break %}oder {% continue %}schreiben zu können, besteht darin, TokenParsers für sie zu schreiben .

Ich habe es für das {% break %}Token im folgenden Code getan . Sie können ohne große Änderungen das Gleiche für die {% 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")
            ;
        }
    }

Dann können Sie einfach verwenden {% break %}, um Schleifen wie folgt zu verlassen:

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

Um noch weiter zu gehen, können Sie Token-Parser für {% continue X %}und {% break X %}(wobei X eine Ganzzahl> = 1 ist) schreiben , um mehrere Schleifen wie in PHP zu beenden / fortzusetzen .

Jules Lamur
quelle
10
Das ist einfach übertrieben. Zweigschleifen sollten Pausen unterstützen und nativ fortgesetzt werden.
Crafter
Dies ist schön, wenn Sie keine Filter verwenden möchten / können.
Daniel Dewhurst
Die squirrelphp/twig-php-syntaxBibliothek bietet {% break %}, {% break n %}und {% continue %}Token.
mts knn
9

Von @NHG Kommentar - funktioniert einwandfrei

{% for post in posts|slice(0,10) %}
Basit
quelle
@Basit wenn Beiträge nach Datum sortiert sind?
Vasilii Suricov
6

Ich habe eine gute Lösung gefunden, um fortzufahren (ich liebe das Pausenbeispiel oben). Hier möchte ich nicht "Agentur" auflisten. In PHP würde ich "weitermachen", aber im Zweig habe ich mir eine Alternative ausgedacht:

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

ODER ich überspringe es einfach, wenn es meinen Kriterien nicht entspricht:

{% for tr in time_reports %}
    {% if not tr.isApproved %}
        .....
    {% endif %}
{% endfor %}
bezahlt für den Christen
quelle