Làm cách nào để sử dụng break hoặc continue trong vòng lặp for trong mẫu Twig?


97

Tôi cố gắng sử dụng một vòng lặp đơn giản, trong mã thực của tôi, vòng lặp này phức tạp hơn và tôi cần breaklặp lại như sau:

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

Làm cách nào để sử dụng hành vi của breakhoặc continuecấu trúc điều khiển PHP trong Twig?

Câu trả lời:


125

Điều này gần như có thể được thực hiện bằng cách đặt một biến mới làm cờ để breaklặp lại:

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

Một ví dụ xấu hơn, nhưng hiệu quả cho 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 %}

Nhưng không có lợi nhuận về hiệu suất, chỉ có hành vi tương tự với các câu lệnh breakcontinuecâu lệnh cài sẵn như trong PHP phẳng.


1
Nó rất hữu ích. Trong trường hợp của tôi, tôi chỉ cần hiển thị / nhận kết quả đầu tiên. Có cách nào trong Twig để chỉ nhận giá trị đầu tiên không? Điều này chỉ dành cho mục đích hiệu suất tốt hơn.
Pathros

1
@pathros Để nhận giá trị đầu tiên, hãy sử dụng firstbộ lọc cành cây: twig.sensiolabs.org/doc/filters/first.html
Victor Bocharsky

1
Yêu thích ghi chú. Đã cố gắng 10 phút cuối cùng của tôi tìm kiếm cái gì đó không thực sự hữu ích: D
Tree Nguyễn

2
Cần lưu ý rằng điều này sẽ không phá vỡ quá trình thực thi mã, mọi thứ bên dưới set break = truesẽ được thực thi trừ khi bạn đặt nó trong một elsecâu lệnh. Xem twigfiddle.com/euio5w
Gus

2
@Gus Yep, đó là lý do tại sao tôi có ý định đặt câu lệnh if đó set break = truevào cuối cùng . Nhưng yeah, nó phụ thuộc vào mã của bạn, do đó, nhờ nhắc đến nó để làm rõ
Victor Bocharsky

120

Từ tài liệu TWIG docs :

Không giống như trong PHP, không thể ngắt hoặc tiếp tục trong một vòng lặp.

Nhưng vẫn:

Tuy nhiên, bạn có thể lọc trình tự trong quá trình lặp lại cho phép bạn bỏ qua các mục.

Ví dụ 1 (đối với danh sách lớn, bạn có thể lọc các bài đăng bằng cách sử dụng slice , slice(start, length)):

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

Ví dụ 2:

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

Bạn thậm chí có thể sử dụng các bộ lọc TWIG của riêng mình cho các điều kiện phức tạp hơn, như:

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

28
Hơn nữa, nếu bạn muốn đạt được break loop sau 10 lần lặp, bạn có thể sử dụng sth như thế:{% for post in posts|slice(0,10) %}
NHG 10/02

5
OK, cảm ơn, tôi có thể đã bỏ lỡ Unlike in PHP, it's not possible to break or continue in a loop.khi đọc tài liệu. Nhưng tôi nghĩ breakcontinuelà một tính năng tốt, mà sẽ cần thêm
Victor Bocharsky

Bạn không thể truy cập biến vòng lặp trong câu lệnh lặp!
Maximus

không hoạt động. danh sách dài, fornên có thể phá vỡ sau lần truy cập đầu tiên. Câu trả lời @VictorBocharsky 's là đúng
Vasilii Suricov

@VasiliiSuricov bạn có thể sử dụng {% for post in posts|slice(0,10) %}cho các danh sách lớn. Xem bình luận đầu tiên của tôi. Tôi cũng đã cập nhật câu trả lời của mình.
NHG

12

Một cách để có thể sử dụng {% break %}hoặc {% continue %}là viết TokenParsers cho chúng.

Tôi đã làm điều đó cho {% break %}mã thông báo trong mã bên dưới. Bạn có thể, không cần sửa đổi nhiều, làm điều tương tự đối với {% 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")
            ;
        }
    }
    

Sau đó, bạn có thể chỉ cần sử dụng {% break %}để thoát ra khỏi các vòng lặp như thế này:

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

Để đi xa hơn nữa, bạn có thể viết trình phân tích cú pháp mã thông báo cho {% continue X %}{% break X %}(trong đó X là số nguyên> = 1) để thoát / tiếp tục nhiều vòng lặp như trong PHP .


10
Đó chỉ là quá mức cần thiết. Các vòng cành phải hỗ trợ các đoạn gãy và tiếp tục nguyên bản.
crafter

Điều này rất tốt nếu bạn không muốn / không thể sử dụng bộ lọc.
Daniel Dewhurst

Các squirrelphp/twig-php-syntaxthư viện cung cấp {% break %}, {% break n %}{% continue %}thẻ.
mts knn

@mtsknn và các tác giả đã sử dụng và cải thiện mã mà tôi đã viết cho câu trả lời này!
Jules Lamur

@JulesLamur, bạn đã nói "@mtsknn và các tác giả", nhưng tôi không liên quan đến thư viện đó.
mts knn

9

Từ bình luận @NHG - hoạt động hoàn hảo

{% for post in posts|slice(0,10) %}

@Basit nếu bài viết được sắp xếp theo ngày?
Vasilii Suricov

6

Tôi đã tìm thấy một công việc tốt để tiếp tục (yêu thích mẫu nghỉ ở trên). Ở đây tôi không muốn liệt kê "cơ quan". Trong PHP, tôi muốn "tiếp tục" nhưng trong thời gian ngắn, tôi đã nghĩ ra một giải pháp thay thế:

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

HOẶC tôi chỉ cần bỏ qua nó nếu nó không đáp ứng tiêu chí của tôi:

{% for tr in time_reports %}
    {% if not tr.isApproved %}
        .....
    {% endif %}
{% endfor %}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.