mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 02:46:29 +00:00
merged branch kotas/master (PR #362)
Commits
-------
f5b9df9 added `if` modifier support to for loop like {% for k in v if k is odd %}
Discussion
----------
[1.2] added `if` modifier support to for loop
Hi.
I'm one of Twig users who love it so much. :)
I've made a patch for adding `if` modifier support to `for` loop in Twig.
Example:
<pre>
{% for n in range(0, 5) if n is odd %}
{{ n }}
{% endfor %}
</pre>
Output:
<pre>
1
3
5
</pre>
This idea comes from Python's list comprehensions. (like `[n for n in range(0,6) if n % 2 == 1]`)
Here is another example.
Before:
<pre>
{% set no_items = true %}
{% for item in items %}
{% if item.available %}
{% set no_items = false %}
{{ item.name }}
{% endif %}
{% endfor %}
{% if no_items %}
No items available.
{% endif %}
</pre>
After:
<pre>
{% for item in items if item.available %}
{{ item.name }}
{% else %}
No items available.
{% endfor %}
</pre>
I hope you like it. But If not, just ignore this request. :)
Thanks.
---------------------------------------------------------------------------
by fabpot at 2011/06/24 01:33:33 -0700
I like it! I will schedule it for inclusion in Twig 1.2. Thanks.
---------------------------------------------------------------------------
by hhamon at 2011/06/26 16:43:26 -0700
+1
---------------------------------------------------------------------------
by nikic at 2011/06/27 00:42:14 -0700
I think the idea per se is interesting. But I just checked all Twig templates in my whole codebase and couldn't find any use for it. What was your practical use for this feature @kotas?
---------------------------------------------------------------------------
by kotas at 2011/06/27 01:52:48 -0700
@nikic My practical use is shown as the second example in the body of this pull request.
Sometimes you need to "filter" an array by seeing its element's property like "item.available" or by some condition like "item.price >= 500".
To do this, in the current version of Twig, you have to write "for" and "if" as nested scopes. And if you want to write {% else %} of the "for" loop, you have to make a temporary variable to see if all elements are skipped.
I think this feature is not essential, but makes templates clean.
---------------------------------------------------------------------------
by nikic at 2011/06/27 06:23:24 -0700
The thing about `else` seems plausible. +1
---------------------------------------------------------------------------
by chucktrukk at 2011/08/02 17:39:16 -0700
Also +1. that makes some very clean template code.
This commit is contained in:
+18
-2
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
class Twig_Node_For extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true), $lineno, $tag);
|
||||
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'ifexpr' => $ifexpr, 'body' => $body, 'else' => $else), array('with_loop' => true), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,6 +72,15 @@ class Twig_Node_For extends Twig_Node
|
||||
->indent()
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('ifexpr')) {
|
||||
$compiler
|
||||
->write("if (")
|
||||
->subcompile($this->getNode('ifexpr'))
|
||||
->raw(") {\n")
|
||||
->indent()
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('else')) {
|
||||
@@ -93,6 +102,13 @@ class Twig_Node_For extends Twig_Node
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $this->getNode('ifexpr')) {
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
|
||||
@@ -37,6 +37,12 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, 'in');
|
||||
$seq = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$ifexpr = null;
|
||||
if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'if')) {
|
||||
$this->parser->getStream()->next();
|
||||
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
|
||||
}
|
||||
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$body = $this->parser->subparse(array($this, 'decideForFork'));
|
||||
if ($this->parser->getStream()->next()->getValue() == 'else') {
|
||||
@@ -55,7 +61,7 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
$valueTarget = $targets->getNode(0);
|
||||
}
|
||||
|
||||
return new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $lineno, $this->getTag());
|
||||
return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
public function decideForFork(Twig_Token $token)
|
||||
|
||||
@@ -21,19 +21,21 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
|
||||
$keyTarget = new Twig_Node_Expression_AssignName('key', 0);
|
||||
$valueTarget = new Twig_Node_Expression_AssignName('item', 0);
|
||||
$seq = new Twig_Node_Expression_Name('items', 0);
|
||||
$ifexpr = new Twig_Node_Expression_Constant(true, 0);
|
||||
$body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$else = null;
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', false);
|
||||
|
||||
$this->assertEquals($keyTarget, $node->getNode('key_target'));
|
||||
$this->assertEquals($valueTarget, $node->getNode('value_target'));
|
||||
$this->assertEquals($seq, $node->getNode('seq'));
|
||||
$this->assertEquals($ifexpr, $node->getNode('ifexpr'));
|
||||
$this->assertEquals($body, $node->getNode('body'));
|
||||
$this->assertEquals(null, $node->getNode('else'));
|
||||
|
||||
$else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', false);
|
||||
$this->assertEquals($else, $node->getNode('else'));
|
||||
}
|
||||
@@ -54,9 +56,10 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
|
||||
$keyTarget = new Twig_Node_Expression_AssignName('key', 0);
|
||||
$valueTarget = new Twig_Node_Expression_AssignName('item', 0);
|
||||
$seq = new Twig_Node_Expression_Name('items', 0);
|
||||
$ifexpr = null;
|
||||
$body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$else = null;
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', false);
|
||||
|
||||
$tests[] = array($node, <<<EOF
|
||||
@@ -74,9 +77,10 @@ EOF
|
||||
$keyTarget = new Twig_Node_Expression_AssignName('k', 0);
|
||||
$valueTarget = new Twig_Node_Expression_AssignName('v', 0);
|
||||
$seq = new Twig_Node_Expression_Name('values', 0);
|
||||
$ifexpr = null;
|
||||
$body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$else = null;
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', true);
|
||||
|
||||
$tests[] = array($node, <<<EOF
|
||||
@@ -115,9 +119,54 @@ EOF
|
||||
$keyTarget = new Twig_Node_Expression_AssignName('k', 0);
|
||||
$valueTarget = new Twig_Node_Expression_AssignName('v', 0);
|
||||
$seq = new Twig_Node_Expression_Name('values', 0);
|
||||
$ifexpr = new Twig_Node_Expression_Constant(true, 0);
|
||||
$body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$else = null;
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', true);
|
||||
|
||||
$tests[] = array($node, <<<EOF
|
||||
\$context['_parent'] = (array) \$context;
|
||||
\$context['_seq'] = twig_ensure_traversable((isset(\$context['values']) ? \$context['values'] : null));
|
||||
\$context['loop'] = array(
|
||||
'parent' => \$context['_parent'],
|
||||
'index0' => 0,
|
||||
'index' => 1,
|
||||
'first' => true,
|
||||
);
|
||||
if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
|
||||
\$length = count(\$context['_seq']);
|
||||
\$context['loop']['revindex0'] = \$length - 1;
|
||||
\$context['loop']['revindex'] = \$length;
|
||||
\$context['loop']['length'] = \$length;
|
||||
\$context['loop']['last'] = 1 === \$length;
|
||||
}
|
||||
foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
|
||||
if (true) {
|
||||
echo (isset(\$context['foo']) ? \$context['foo'] : null);
|
||||
++\$context['loop']['index0'];
|
||||
++\$context['loop']['index'];
|
||||
\$context['loop']['first'] = false;
|
||||
if (isset(\$context['loop']['length'])) {
|
||||
--\$context['loop']['revindex0'];
|
||||
--\$context['loop']['revindex'];
|
||||
\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
|
||||
}
|
||||
}
|
||||
}
|
||||
\$_parent = \$context['_parent'];
|
||||
unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
|
||||
\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));
|
||||
EOF
|
||||
);
|
||||
|
||||
$keyTarget = new Twig_Node_Expression_AssignName('k', 0);
|
||||
$valueTarget = new Twig_Node_Expression_AssignName('v', 0);
|
||||
$seq = new Twig_Node_Expression_Name('values', 0);
|
||||
$ifexpr = null;
|
||||
$body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0);
|
||||
$node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0);
|
||||
$node->setAttribute('with_loop', true);
|
||||
|
||||
$tests[] = array($node, <<<EOF
|
||||
|
||||
Reference in New Issue
Block a user