我尝试使用一个简单的循环,在我的实际代码中这个循环更加复杂,我需要用break
来结束迭代:
{% for post in posts %}
{% if post.id == 10 %}
{# break #}
{% endif %}
<h2>{{ post.heading }}</h2>
{% endfor %}
我该如何在Twig中运用PHP控制结构break
或continue
的行为?
可以通过设置一个新的变量作为标记,在迭代时用 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 %}
但是没有性能收益,与内置的
break
和continue
语句类似,只有相似的行为,就像在基础的 PHP 中一样。
来自 TWIG 2.x 文档文档:
与 PHP 不同的是,在循环中无法使用 break 或 continue。
但是:
您可以在迭代期间过滤序列,从而跳过项目。
示例1(对于巨大的列表,您可以使用slice过滤器来过滤帖子,slice(start, length)
):
{% for post in posts|slice(0,10) %}
<h2>{{ post.heading }}</h2>
{% endfor %}
例2同样适用于TWIG 3.0:
{% for post in posts if post.id < 10 %}
<h2>{{ post.heading }}</h2>
{% endfor %}
您甚至可以使用自己的TWIG筛选器来处理更复杂的条件,例如:
{% for post in posts|onlySuperPosts %}
<h2>{{ post.heading }}</h2>
{% endfor %}
{% for post in posts|slice(0,10) %}
。 - NHG{% break %}
或 {% continue %}
的方法是编写相应的 TokenParser
。我已经为 {% break %}
token 编写了下面的代码。您可以进行少量修改,以同样的方式实现 {% 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 %}
和 {% break X %}
(其中X是大于等于1的整数),以像PHP一样跳出/继续多个循环。squirrelphp/twig-php-syntax
库 提供了 {% break %}
, {% break n %}
和 {% continue %}
标记。 - Matias Kinnunen来自@NHG评论 - 完美运作
{% for post in posts|slice(0,10) %}
我发现了一个很好的解决方案来代替"continue"(喜欢上面的break示例)。 在这里,我不想列出"agency"。在PHP中,我会使用"continue",但在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 %}
set break = true
放在else
语句中,否则下面的任何内容都将被执行。请参阅 https://twigfiddle.com/euio5w。 - Gusset break = true
的 if 语句的原因。但是,这取决于你的代码,所以感谢你提到它以澄清。 - Victor Bocharskyif not break
的问题。我收到了这个错误Uncaught Twig\Error\SyntaxError: Unexpected token "name" of value "if"
。 - ZenithS