Browse Source

[Form] Fixed variable scope when entering nested form helpers

The consequence of this commit is that variables are accessible that have been passed to a surrounding form helper.

Example template:

{% block my_widget_label %}
    <label>{{ label }}
{% endblock %}

{% block my_widget_row %}
    {# It is not necessary to explicitely pass through the label variable #}
    {{ form_label(form) }}
    {{ form_widget(form) }}
{% endblock %}

Example usage:

{{ form_row(form.mywidget, { 'label': 'My Widget' }) }}
Bernhard Schussek 14 years ago
parent
commit
eb50d766da

+ 11 - 1
src/Symfony/Bridge/Twig/Extension/FormExtension.php

@@ -27,10 +27,12 @@ class FormExtension extends \Twig_Extension
     protected $templates;
     protected $environment;
     protected $themes;
+    protected $varStack;
 
     public function __construct(array $resources = array())
     {
         $this->themes = new \SplObjectStorage();
+        $this->varStack = new \SplObjectStorage();
         $this->resources = $resources;
     }
 
@@ -166,7 +168,15 @@ class FormExtension extends \Twig_Extension
                     $view->setRendered();
                 }
 
-                return $templates[$block]->renderBlock($block, array_merge($view->all(), $variables));
+                $this->varStack[$view] = array_replace(
+                    $view->all(),
+                    isset($this->varStack[$view]) ? $this->varStack[$view] : array(),
+                    $variables
+                );
+
+                $html = $templates[$block]->renderBlock($block, $this->varStack[$view]);
+
+                return $html;
             }
         }
 

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php

@@ -1,4 +1,4 @@
 <?php echo str_replace('{{ widget }}',
-    $view['form']->render('FrameworkBundle:Form:number_widget.html.php'),
+    $view['form']->render($form, 'FrameworkBundle:Form:number_widget.html.php'),
     $money_pattern
 ) ?>

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php

@@ -1 +1 @@
-<?php echo $view['form']->render('FrameworkBundle:Form:number_widget.html.php') ?> %
+<?php echo $view['form']->render($form, 'FrameworkBundle:Form:number_widget.html.php') ?> %

+ 17 - 10
src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php

@@ -28,11 +28,14 @@ class FormHelper extends Helper
 
     protected $engine;
 
-    protected $varStack = array();
+    protected $varStack;
+
+    protected $viewStack = array();
 
     public function __construct(EngineInterface $engine)
     {
         $this->engine = $engine;
+        $this->varStack = new \SplObjectStorage();
     }
 
     public function attributes()
@@ -40,8 +43,9 @@ class FormHelper extends Helper
         $html = '';
         $attr = array();
 
-        if (count($this->varStack) > 0) {
-            $vars = end($this->varStack);
+        if (count($this->viewStack) > 0) {
+            $view = end($this->viewStack);
+            $vars = $this->varStack[$view];
 
             if (isset($vars['attr'])) {
                 $attr = $vars['attr'];
@@ -120,19 +124,22 @@ class FormHelper extends Helper
             $view->setRendered();
         }
 
-        return $this->render($template, array_merge($view->all(), $variables));
+        return $this->render($view, $template, $variables);
     }
 
-    public function render($template, array $variables = array())
+    public function render(FormView $view, $template, array $variables = array())
     {
-        array_push($this->varStack, array_merge(
-            count($this->varStack) > 0 ? end($this->varStack) : array(),
+        $this->varStack[$view] = array_replace(
+            $view->all(),
+            isset($this->varStack[$view]) ? $this->varStack[$view] : array(),
             $variables
-        ));
+        );
+
+        array_push($this->viewStack, $view);
 
-        $html = $this->engine->render($template, end($this->varStack));
+        $html = $this->engine->render($template, $this->varStack[$view]);
 
-        array_pop($this->varStack);
+        array_pop($this->viewStack);
 
         return $html;
     }

+ 1 - 1
src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/_text_id_widget.html.php

@@ -1,3 +1,3 @@
 <div id="container">
-    <?php echo $view['form']->render('FrameworkBundle:Form:text_widget.html.php') ?>
+    <?php echo $view['form']->render($form, 'FrameworkBundle:Form:text_widget.html.php') ?>
 </div>

+ 15 - 0
tests/Symfony/Tests/Component/Form/AbstractDivLayoutTest.php

@@ -35,6 +35,21 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
         );
     }
 
+    public function testRowForwardsVariables()
+    {
+        $view = $this->factory->createNamed('text', 'name')->createView();
+        $html = $this->renderRow($view, array('label' => 'foo&bar'));
+
+        $this->assertMatchesXpath($html,
+'/div
+    [
+        ./label[@for="name"][.="[trans]foo&bar[/trans]"]
+        /following-sibling::input[@id="name"]
+    ]
+'
+        );
+    }
+
     public function testRepeatedRow()
     {
         $form = $this->factory->createNamed('repeated', 'name');