Bagaimana saya bisa menggunakan break atau melanjutkan dalam for loop pada template Twig?

97

Saya mencoba menggunakan loop sederhana, dalam kode asli saya, loop ini lebih kompleks, dan saya perlu breakiterasi ini seperti:

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

Bagaimana cara menggunakan perilaku breakatau continuestruktur kontrol PHP di Twig?

Victor Bocharsky
sumber

Jawaban:

126

Ini hampir bisa dilakukan dengan menetapkan variabel baru sebagai tanda untuk melakukan breakiterasi:

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

Contoh yang lebih jelek, tetapi berfungsi untuk 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 %}

Tetapi tidak ada keuntungan kinerja, hanya perilaku yang mirip dengan built-in breakdan continuepernyataan seperti di PHP datar.

Victor Bocharsky
sumber
1
Ini berguna. Dalam kasus saya, saya hanya perlu menunjukkan / mendapatkan hasil pertama. Apakah ada cara di Twig untuk mendapatkan nilai pertama saja? Ini hanya untuk tujuan kinerja yang lebih baik.
Pathros
1
@pathros Untuk mendapatkan nilai pertama, gunakan firstfilter ranting: twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky
1
Suka catatannya. Telah mencoba 10 menit terakhir saya menemukan sesuatu yang tidak terlalu membantu: D
Tree Nguyen
2
Perlu dicatat bahwa ini tidak akan merusak eksekusi kode, apa pun di bawah set break = trueini akan dieksekusi kecuali Anda memasukkannya ke dalam elsepernyataan. Lihat twigfiddle.com/euio5w
Gus
2
@Gus Yap, itulah mengapa saya bermaksud untuk meletakkan pernyataan if itu set break = truedi bagian paling akhir . Tapi ya, itu tergantung pada kode Anda, jadi terima kasih telah menyebutkannya untuk memperjelas
Victor Bocharsky
121

Dari dokumen TWIG dokumen :

Tidak seperti di PHP, tidak mungkin untuk memutuskan atau melanjutkan dalam satu putaran.

Tetapi tetap saja:

Namun Anda dapat memfilter urutan selama iterasi yang memungkinkan Anda melewati item.

Contoh 1 (untuk daftar besar Anda dapat memfilter posting menggunakan slice , slice(start, length)):

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

Contoh 2:

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

Anda bahkan dapat menggunakan filter TWIG sendiri untuk kondisi yang lebih kompleks, seperti:

{% for post in posts|onlySuperPosts %}
    <h2>{{ post.heading }}</h2>
{% endfor %}
NHG
sumber
28
Selain itu jika Anda ingin mencapai break loop setelah 10 iterasi, Anda dapat menggunakan sth seperti itu:{% for post in posts|slice(0,10) %}
NHG
5
Oke, terima kasih, saya mungkin ketinggalan Unlike in PHP, it's not possible to break or continue in a loop.saat membaca dokumen. Tapi saya pikir breakdan continueini adalah fitur yang bagus, yang perlu ditambahkan
Victor Bocharsky
Anda tidak dapat mengakses variabel loop dalam pernyataan loop!
Maximus
tidak bekerja. daftar panjang, forharus bisa dipecahkan setelah pukulan pertama. Jawaban @VictorBocharsky benar
Vasilii Suricov
@VasiliiSuricov dapat Anda gunakan {% for post in posts|slice(0,10) %}untuk daftar besar. Lihat komentar pertama saya. Saya juga telah memperbarui jawaban saya.
NHG
12

Cara untuk dapat menggunakan {% break %}atau {% continue %}menulis TokenParseruntuk mereka.

Saya melakukannya untuk {% break %}token dalam kode di bawah ini. Anda dapat, tanpa banyak modifikasi, melakukan hal yang sama untuk 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")
            ;
        }
    }

Kemudian Anda dapat menggunakan {% break %}untuk keluar dari loop seperti ini:

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

Untuk melangkah lebih jauh, Anda dapat menulis parser token {% continue X %}dan {% break X %}(di mana X adalah integer> = 1) untuk keluar / melanjutkan beberapa loop seperti di PHP .

Jules Lamur
sumber
10
Itu terlalu berlebihan. Twig loop harus mendukung jeda dan berlanjut secara native.
perajin
Ini bagus jika Anda tidak ingin / tidak dapat menggunakan filter.
Daniel Dewhurst
The squirrelphp/twig-php-syntaxperpustakaan menyediakan {% break %}, {% break n %}dan {% continue %}token.
mts knn
@mtsknn dan penulis menggunakan dan meningkatkan kode yang saya tulis untuk jawaban ini!
Jules Lamur
@JulesLamur, Anda mengatakan "@mtsknn dan penulisnya," tetapi saya tidak terlibat dengan perpustakaan itu.
mts knn
9

Dari komentar @NHG - bekerja dengan sempurna

{% for post in posts|slice(0,10) %}
Basit
sumber
@Basit apakah posting diurutkan berdasarkan tanggal?
Vasilii Suricov
6

Saya telah menemukan solusi yang bagus untuk melanjutkan (suka contoh istirahat di atas). Di sini saya tidak ingin mencantumkan "agensi". Di PHP saya akan "melanjutkan" tetapi di ranting, saya menemukan alternatif:

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

ATAU Saya melewatkannya jika tidak memenuhi kriteria saya:

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