Twigテンプレートでforループ内でbreakまたはcontinueを使用するにはどうすればよいですか?


97

私は単純なループを使用しようとしていますが、実際のコードではこのループはより複雑であり、次のbreakような反復が必要です。

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

どのように私はの動作を使用することができますbreakcontinue小枝でPHPの制御構造のか?

回答:


125

これは、新しい変数を反復するフラグとして設定することでほぼ実行できますbreak

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

醜いが、のための実用的な例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 %}

しかしパフォーマンス上の利益はなく、フラットなPHPのような組み込みbreakおよびcontinueステートメントと同様の動作のみです。


1
それは便利です。私の場合、最初の結果を表示/取得するだけです。Twigに最初の値だけを取得する方法はありますか?これは、パフォーマンスを向上させるためだけのものです。
Pathros、2016

1
@pathros最初の値を取得するには、firsttwigフィルターを使用します。twig.sensiolabs.org
Victor Bocharsky 2016

1
メモが大好きです。本当に役に立たないものを見つけるために最後の10分間を試してみました:D
Tree Nguyen

2
これはコードの実行を中断しないことに注意する価値set break = trueがありますelse。ステートメントに入れない限り、以下のすべてが実行されます。twigfiddle.com/euio5wを
Gus

2
なぜ私はそのif文を置くことを意味しています@Gusうん、set break = true非常に終わり。しかし、そうです、それはあなたのコードに依存しますので、明確にするためにそれを言及してくれてありがとう
Victor Bocharsky

120

docs TWIG docsから:

PHPとは異なり、ループ内で中断または続行することはできません。

それでも:

ただし、反復中にシーケンスをフィルタリングして、アイテムをスキップすることができます。

例1(巨大なリストの場合、sliceを使用して投稿をフィルタリングできますslice(start, length)):

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

例2:

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

以下のような、より複雑な条件に対して独自のTWIGフィルターを使用することもできます。

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

28
:あなたは10反復後ブレークループをachiveしたい場合はまた、あなたがそのようなSTH使用することができます{% for post in posts|slice(0,10) %}
NHG

5
わかりました。ありがとうございUnlike in PHP, it's not possible to break or continue in a loop.ます。ドキュメントを読んだときに、おそらく見逃してしまいました。しかし、私はこれを追加する必要がある優れた機能だbreakと思いますcontinue
Victor Bocharsky

ループステートメントでループ変数にアクセスできません!
Maximus

動作しません。長いリストfor。最初のヒット後に分割可能でなければなりません。@VictorBocharskyの答えは正しい
Vasilii Suricov

{% for post in posts|slice(0,10) %}巨大なリストに使用できる@VasiliiSuricov 。私の最初のコメントを見てください。回答も更新しました。
NHG

12

を使用できるようにする方法、{% break %}またはs {% continue %}を作成TokenParserする方法。

{% break %}以下のコードのトークンに対してそれを行いました。変更をほとんど加えずに、に対して同じことを行うことができ{% 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")
            ;
        }
    }
    

次に、単にを使用{% break %}して、次のようなループから抜け出すことができます。

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

さらに進むには、{% continue X %}and {% break X %}(ここでXは整数> = 1)のトークンパーサーを記述して、PHPのように複数のループ取得/継続することができます


10
それはやり過ぎです。Twigループはブレークをサポートし、ネイティブで継続します。
クラフター

これは、フィルターを使用したくない、または使用できない場合に便利です。
Daniel Dewhurst 2018年

squirrelphp/twig-php-syntaxライブラリが提供{% break %}{% break n %}および{% continue %}トークン。
mts knn

@mtsknnと著者は、私がこの回答のために書いたコードを使用して改善しました!
Jules Lamur

@JulesLamur、あなたは「@mtsknnと作者」と言ったが、私はそのライブラリには関わっていない。
mts knn


6

続行するための適切な回避策を見つけました(上記の休憩のサンプルが大好きです)。ここでは「代理店」を挙げたくない。PHPでは「継続」するつもりですが、小枝では代替案を考え出しました。

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

または、基準を満たしていない場合はスキップします。

{% for tr in time_reports %}
    {% if not tr.isApproved %}
        .....
    {% endif %}
{% endfor %}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.