ฉันจะใช้การแบ่งหรือดำเนินการต่อภายในสำหรับลูปในเทมเพลต Twig ได้อย่างไร


100

ฉันพยายามใช้ลูปง่ายๆในโค้ดจริงของฉันลูปนี้ซับซ้อนกว่าและฉันต้องการการbreakวนซ้ำเช่นนี้:

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

ฉันจะใช้พฤติกรรมของbreakหรือcontinueโครงสร้างควบคุม PHP ใน Twig ได้อย่างไร

คำตอบ:


132

นี้สามารถเกือบทำได้โดยการตั้งค่าตัวแปรใหม่เป็นธงไปbreakiterating:

{% 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 %}

แต่ไม่มีผลกำไรจากการดำเนินงานมีเพียงพฤติกรรมที่คล้ายกันในตัวbreakและcontinueงบเช่นใน PHP แบบแบน


1
มันมีประโยชน์ ในกรณีของฉันฉันแค่ต้องแสดง / รับผลลัพธ์แรก มีวิธีใดใน Twig ที่จะได้รับเพียงค่าแรก? นี่เป็นเพียงเพื่อจุดประสงค์ด้านประสิทธิภาพที่ดีขึ้นเท่านั้น
Pathros

1
@pathros ในการรับค่าแรกให้ใช้firstตัวกรอง twig: twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky

1
รักโน้ต พยายาม 10 นาทีสุดท้ายของฉันเพื่อค้นหาสิ่งที่ไม่เป็นประโยชน์จริงๆ: D
Tree Nguyen

2
เป็นที่น่าสังเกตว่าสิ่งนี้จะไม่ทำลายการเรียกใช้โค้ดสิ่งใด ๆ ด้านล่างset break = trueจะถูกดำเนินการเว้นแต่คุณจะใส่ไว้ในelseคำสั่ง ดูtwigfiddle.com/euio5w
Gus

2
@ กัสใช่นั่นเป็นเหตุผลว่าทำไมฉันถึงตั้งใจที่จะใส่คำสั่งset break = trueนั้นลงไป แต่ใช่มันขึ้นอยู่กับรหัสของคุณดังนั้นขอบคุณที่กล่าวถึงมันเพื่อชี้แจง
Victor Bocharsky

125

จาก docs TWIG docs :

ซึ่งแตกต่างจาก PHP ตรงที่ไม่สามารถหยุดทำงานหรือเล่นต่อแบบวนซ้ำได้

แต่ยังคง:

อย่างไรก็ตามคุณสามารถกรองลำดับระหว่างการทำซ้ำซึ่งช่วยให้คุณข้ามรายการได้

ตัวอย่างที่ 1 (สำหรับรายการใหญ่คุณสามารถกรองโพสต์โดยใช้ชิ้น , 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 ครั้งคุณสามารถใช้ 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

คุณไม่สามารถเข้าถึงตัวแปรลูปในคำสั่งลูปได้!
สังฆ

ไม่ทำงาน รายการยาวforควรแตกได้หลังจากตีครั้งแรก คำตอบของ @VictorBocharsky ถูกต้อง
Vasilii Suricov

@VasiliiSuricov คุณสามารถใช้{% for post in posts|slice(0,10) %}สำหรับรายการขนาดใหญ่ ดูความคิดเห็นแรกของฉัน ฉันยังอัปเดตคำตอบของฉัน
NHG

12

วิธีที่จะใช้งานได้{% break %}หรือ{% continue %}เขียนTokenParsers สำหรับพวกเขา

ฉันทำเพื่อ{% 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 %}

ที่จะไปให้ดียิ่งขึ้นคุณอาจเขียน parsers โทเค็นสำหรับ{% continue X %}และ{% break X %}(โดยที่ X เป็นจำนวนเต็ม> = 1) ได้รับการออก / ยังคงลูปหลายตัวเช่นใน PHP


11
นั่นเป็นเพียงการใช้มากเกินไป Twig loops ควรรองรับการหยุดพักและดำเนินต่อไปโดยกำเนิด
Crafter

นี่เป็นสิ่งที่ดีถ้าคุณไม่ต้องการ / ไม่สามารถใช้ตัวกรอง
Daniel Dewhurst

squirrelphp/twig-php-syntaxห้องสมุดให้{% break %}, {% break n %}และ{% continue %}ราชสกุล
mts knn

@mtsknn และผู้เขียนใช้และปรับปรุงโค้ดที่ฉันเขียนสำหรับคำตอบนี้!
Jules Lamur

@JulesLamur คุณพูดว่า "@mtsknn และผู้เขียน" แต่ฉันไม่ได้เกี่ยวข้องกับห้องสมุดนั้น
mts knn


6

ฉันพบวิธีแก้ไขที่ดีในการดำเนินการต่อ (ชอบตัวอย่างการแบ่งด้านบน) ที่นี่ฉันไม่ต้องการแสดงรายการ "หน่วยงาน" ใน PHP ฉันจะ "ดำเนินการต่อ" แต่ใน twig ฉันมีทางเลือกอื่น:

{% 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.