Added joined by statement to for loops
authorJordi Boggiano <j.boggiano@seld.be>
Sun, 28 Nov 2010 15:00:01 +0000 (16:00 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Sun, 28 Nov 2010 16:10:03 +0000 (17:10 +0100)
doc/02-Twig-for-Template-Designers.markdown
lib/Twig/Node/For.php
lib/Twig/TokenParser/For.php
test/Twig/Tests/Node/ForTest.php

index abc5038..91880c2 100644 (file)
@@ -581,6 +581,20 @@ You can also access both keys and values:
 >On Twig before 0.9.3, you need to use the `items` filter to access both the
 >keys and values (`{% for key, value in users|items %}`).
 
+To conveniently display comma-separated lists or things alike, you can use
+`joined by ", "` at the end of the loop statement. Of course this is not
+limited to commas.
+
+    [twig]
+    <h1>Members</h1>
+    <p>
+      {% for key in users|keys joined by ", " %}{{ key }}{% endfor %}
+    </p>
+
+>**NOTE**
+>This way you don't have to check if the item is the first or last, the comma
+>will only be added in between two items.
+
 ### If
 
 The `if` statement in Twig is comparable with the if statements of PHP. In the
index 2f7a2df..38ad626 100644 (file)
@@ -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, $withLoop = false, $lineno, $tag = null)
+    public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $withLoop = false, Twig_Node_Expression $joinedBy = null, $lineno, $tag = null)
     {
-        parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => $withLoop), $lineno, $tag);
+        parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else, 'joined_with' => $joinedBy), array('with_loop' => $withLoop), $lineno, $tag);
     }
 
     /**
@@ -68,6 +68,10 @@ class Twig_Node_For extends Twig_Node
             ;
         }
 
+        if (null !== $this->getNode('joined_with')) {
+            $compiler->write("\$context['_first_iteration'] = true;\n");
+        }
+
         $compiler
             ->write("foreach (\$context['_seq'] as ")
             ->subcompile($this->getNode('key_target'))
@@ -81,6 +85,21 @@ class Twig_Node_For extends Twig_Node
             $compiler->write("\$context['_iterated'] = true;\n");
         }
 
+        if (null !== $this->getNode('joined_with')) {
+            $compiler
+                ->write("if (\$context['_first_iteration']) {\n")
+                ->indent()
+                ->write("\$context['_first_iteration'] = false;\n")
+                ->outdent()
+                ->write("} else {\n")
+                ->indent()
+                ->write("echo ")
+                ->subcompile($this->getNode('joined_with'))
+                ->raw(";\n")
+                ->outdent()
+                ->write("}\n");
+        }
+
         $compiler->subcompile($this->getNode('body'));
 
         if ($this->getAttribute('with_loop')) {
index 4fec054..42b71af 100644 (file)
@@ -32,6 +32,13 @@ class Twig_TokenParser_For extends Twig_TokenParser
             $withLoop = false;
         }
 
+        $joinedBy = null;
+        if ($this->parser->getStream()->test('joined')) {
+            $this->parser->getStream()->next();
+            $this->parser->getStream()->expect('by');
+            $joinedBy = $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') {
@@ -50,7 +57,7 @@ class Twig_TokenParser_For extends Twig_TokenParser
             $valueTarget = $targets->getNode(0);
         }
 
-        return new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, $lineno, $this->getTag());
+        return new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, $joinedBy, $lineno, $this->getTag());
     }
 
     public function decideForFork($token)
index d99109f..71144f8 100644 (file)
@@ -24,7 +24,7 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
         $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
         $else = null;
         $withLoop = false;
-        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, null, 0);
 
         $this->assertEquals($keyTarget, $node->getNode('key_target'));
         $this->assertEquals($valueTarget, $node->getNode('value_target'));
@@ -35,7 +35,7 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
         $this->assertEquals($withLoop, $node->getAttribute('with_loop'));
 
         $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
-        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, null, 0);
         $this->assertEquals($else, $node->getNode('else'));
     }
 
@@ -58,7 +58,7 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
         $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
         $else = null;
         $withLoop = false;
-        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, null, 0);
 
         $tests[] = array($node, <<<EOF
 \$context['_parent'] = (array) \$context;
@@ -78,7 +78,8 @@ EOF
         $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0);
         $else = null;
         $withLoop = true;
-        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0);
+        $joinedBy = new Twig_Node_Expression_Constant(', ', 0);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, $joinedBy, 0);
 
         $tests[] = array($node, <<<EOF
 \$context['_parent'] = (array) \$context;
@@ -97,7 +98,13 @@ if (\$countable) {
     \$context['loop']['length'] = \$length;
     \$context['loop']['last'] = 1 === \$length;
 }
+\$context['_first_iteration'] = true;
 foreach (\$context['_seq'] as \$context['k'] => \$context['v']) {
+    if (\$context['_first_iteration']) {
+        \$context['_first_iteration'] = false;
+    } else {
+        echo ", ";
+    }
     echo (isset(\$context['foo']) ? \$context['foo'] : null);
     ++\$context['loop']['index0'];
     ++\$context['loop']['index'];
@@ -120,7 +127,7 @@ EOF
         $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);
         $withLoop = true;
-        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0);
+        $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, null, 0);
 
         $tests[] = array($node, <<<EOF
 \$context['_parent'] = (array) \$context;