Selaa lähdekoodia

[Form] Improved rendering

Fields are not available in the templates anymore. Instead, all required information can be
accessed through view variables.

Example usage of helpers and variables in a form theme:

// use the label helper
{{ this.label('my label') }}

// use the label variable
{{ this.vars.label }}
{{ label }}

Example usage of helpers and variables in a normal template:

// use the label helper
{{ field.label('my label') }}

// use the label variable
{{ field.vars.label }}
Bernhard Schussek 14 vuotta sitten
vanhempi
commit
02d2121dcd

+ 34 - 32
src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig

@@ -1,32 +1,34 @@
 {% block row %}
 {% spaceless %}
     <div>
-        {{ field.renderer.label }}
-        {{ field.renderer.errors }}
-        {{ field.renderer.widget }}
+        {{ this.label }}
+        {{ this.errors }}
+        {{ this.widget }}
     </div>
 {% endspaceless %}
 {% endblock row %}
 
 {% block errors %}
 {% spaceless %}
-    {% if field.hasErrors %}
+    {% if errors|length > 0 %}
     <ul>
-        {% for error in field.errors %}
+        {% for error in errors %}
             <li>{% trans error.messageTemplate with error.messageParameters from 'validators' %}</li>
         {% endfor %}
     </ul>
-    {% endif %}
+    {% endif %}  	
 {% endspaceless %}
 {% endblock errors %}
 
-{% block hidden %}
+{% block rest %}
 {% spaceless %}
-    {% for child in field.allHiddenFields %}
-        {{ child.renderer.widget }}
+    {% for field in fields %}
+        {% if field.vars.hidden %}
+            {{ field.widget }}
+        {% endif %}
     {% endfor %}
 {% endspaceless %}
-{% endblock hidden %}
+{% endblock rest %}
 
 {% block label %}
 {% spaceless %}
@@ -36,7 +38,7 @@
 
 {% block attributes %}
 {% spaceless %}
-    id="{{ id }}" name="{{ name }}"{% if class %} class="{{ class }}"{% endif %}{% if field.disabled %} disabled="disabled"{% endif %}{% if field.required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if size %} size="{{ size }}"{% endif %}
+    id="{{ id }}" name="{{ name }}"{% if class %} class="{{ class }}"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if size %} size="{{ size }}"{% endif %}
 {% endspaceless %}
 {% endblock attributes %}
 
@@ -49,11 +51,11 @@
 
 {% block form__widget %}
 {% spaceless %}
-    {{ field.renderer.errors }}
-    {% for child in field.visibleFields %}
-        {{ child.renderer.row }}
+    {{ this.errors }}
+    {% for field in visible_fields %}
+        {{ field.row }}
     {% endfor %}
-    {{ field.renderer.hidden }}
+    {{ this.rest }}
 {% endspaceless %}
 {% endblock form__widget %}
 
@@ -107,13 +109,13 @@
 {% block choice__widget %}
 {% spaceless %}
     {% if expanded %}
-        {% for choice, child in field %}
-            {{ child.renderer.widget }}
-            {{ child.renderer.label(choice_list.label(choice)) }}
+        {% for choice, field in fields %}
+            {{ field.widget }}
+            {{ field.label(choice_list.label(choice)) }}
         {% endfor %}
     {% else %}
     <select {{ block('attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
-        {% if not field.required %}
+        {% if not required %}
             <option value="">{{ empty_value }}</option>
         {% endif %}
         {% if preferred_choices|length > 0 %}
@@ -130,22 +132,22 @@
 
 {% block checkbox__widget %}
 {% spaceless %}
-    <input type="checkbox" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if field.data %} checked="checked"{% endif %} />
+    <input type="checkbox" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
 {% endspaceless %}
 {% endblock checkbox__widget %}
 
 {% block radio__widget %}
 {% spaceless %}
-    <input type="radio" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if field.data %} checked="checked"{% endif %} />
+    <input type="radio" {{ block('attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
 {% endspaceless %}
 {% endblock radio__widget %}
 
 {% block date_time__widget %}
 {% spaceless %}
-    {{ field.date.renderer.errors }}
-    {{ field.time.renderer.errors }}
-    {{ field.date.renderer.widget }}
-    {{ field.time.renderer.widget }}
+    {{ fields.date.errors }}
+    {{ fields.time.errors }}
+    {{ fields.date.widget }}
+    {{ fields.time.widget }}
 {% endspaceless %}
 {% endblock date_time__widget %}
 
@@ -155,9 +157,9 @@
         {{ block('text__widget') }}
     {% else %}
         {{ date_pattern|replace({ 
-            '{{ year }}': field.year.renderer.widget, 
-            '{{ month }}': field.month.renderer.widget, 
-            '{{ day }}': field.day.renderer.widget 
+            '{{ year }}': fields.year.widget, 
+            '{{ month }}': fields.month.widget, 
+            '{{ day }}': fields.day.widget 
         })|raw }}
     {% endif %}
 {% endspaceless %}
@@ -165,7 +167,7 @@
 
 {% block time__widget %}
 {% spaceless %}
-    {{ field.hour.renderer.widget({ 'size': '1' }) }}:{{ field.minute.renderer.widget({ 'size': '1' }) }}{% if with_seconds %}:{{ field.second.renderer.widget({ 'size': '1' }) }}{% endif %}
+    {{ fields.hour.widget({ 'size': '1' }) }}:{{ fields.minute.widget({ 'size': '1' }) }}{% if with_seconds %}:{{ fields.second.widget({ 'size': '1' }) }}{% endif %}
 {% endspaceless %}
 {% endblock time__widget %}
 
@@ -205,9 +207,9 @@
 
 {% block file__widget %}
 {% spaceless %}
-    {{ field.file.renderer.widget }}
-    {{ field.token.renderer.widget }}
-    {{ field.name.renderer.widget }}
+    {{ fields.file.widget }}
+    {{ fields.token.widget }}
+    {{ fields.name.widget }}
 {% endspaceless %}
 {% endblock file__widget %}
 

+ 0 - 70
src/Symfony/Component/Form/Form.php

@@ -228,76 +228,6 @@ class Form extends Field implements \IteratorAggregate, FormInterface
         return $this->fields;
     }
 
-    /**
-     * Returns an array of visible fields from the current schema.
-     *
-     * @return array
-     */
-    public function getVisibleFields()
-    {
-        return $this->getFieldsByVisibility(false, false);
-    }
-
-    /**
-     * Returns an array of visible fields from the current schema.
-     *
-     * This variant of the method will recursively get all the
-     * fields from the nested forms or field groups
-     *
-     * @return array
-     */
-    public function getAllVisibleFields()
-    {
-        return $this->getFieldsByVisibility(false, true);
-    }
-
-    /**
-     * Returns an array of hidden fields from the current schema.
-     *
-     * @return array
-     */
-    public function getHiddenFields()
-    {
-        return $this->getFieldsByVisibility(true, false);
-    }
-
-    /**
-     * Returns an array of hidden fields from the current schema.
-     *
-     * This variant of the method will recursively get all the
-     * fields from the nested forms or field groups
-     *
-     * @return array
-     */
-    public function getAllHiddenFields()
-    {
-        return $this->getFieldsByVisibility(true, true);
-    }
-
-    /**
-     * Returns a filtered array of fields from the current schema.
-     *
-     * @param Boolean $hidden Whether to return hidden fields only or visible fields only
-     * @param Boolean $recursive Whether to recur through embedded schemas
-     *
-     * @return array
-     */
-    protected function getFieldsByVisibility($hidden, $recursive)
-    {
-        $fields = array();
-        $hidden = (Boolean)$hidden;
-
-        foreach ($this->fields as $field) {
-            if ($field instanceof Form && $recursive) {
-                $fields = array_merge($fields, $field->getFieldsByVisibility($hidden, $recursive));
-            } else if ($hidden === $field->isHidden()) {
-                $fields[] = $field;
-            }
-        }
-
-        return $fields;
-    }
-
     /**
      * Initializes the field group with an object to operate on
      *

+ 11 - 9
src/Symfony/Component/Form/FormFactory.php

@@ -25,16 +25,16 @@ use Symfony\Component\Form\DataProcessor\FileUploader;
 use Symfony\Component\Form\FieldFactory\FieldFactoryInterface;
 use Symfony\Component\Form\Renderer\DefaultRenderer;
 use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
-use Symfony\Component\Form\Renderer\Plugin\IdPlugin;
-use Symfony\Component\Form\Renderer\Plugin\NamePlugin;
+use Symfony\Component\Form\Renderer\Plugin\FieldPlugin;
+use Symfony\Component\Form\Renderer\Plugin\FormPlugin;
 use Symfony\Component\Form\Renderer\Plugin\ParameterPlugin;
 use Symfony\Component\Form\Renderer\Plugin\ChoicePlugin;
 use Symfony\Component\Form\Renderer\Plugin\ParentNamePlugin;
 use Symfony\Component\Form\Renderer\Plugin\DatePatternPlugin;
 use Symfony\Component\Form\Renderer\Plugin\MoneyPatternPlugin;
-use Symfony\Component\Form\Renderer\Plugin\ValuePlugin;
 use Symfony\Component\Form\Renderer\Plugin\PasswordValuePlugin;
 use Symfony\Component\Form\Renderer\Plugin\SelectMultipleNamePlugin;
+use Symfony\Component\Form\Renderer\Plugin\CheckedPlugin;
 use Symfony\Component\Form\ValueTransformer\BooleanToStringTransformer;
 use Symfony\Component\Form\ValueTransformer\NumberToLocalizedStringTransformer;
 use Symfony\Component\Form\ValueTransformer\IntegerToLocalizedStringTransformer;
@@ -130,10 +130,7 @@ class FormFactory
             ->setValueTransformer($options['value_transformer'])
             ->setNormalizationTransformer($options['normalization_transformer'])
             ->setRenderer(new DefaultRenderer($this->theme, $options['template']))
-            ->addRendererPlugin(new IdPlugin($field))
-            ->addRendererPlugin(new NamePlugin($field))
-            ->addRendererPlugin(new ValuePlugin($field))
-            ->setRendererVar('field', $field)
+            ->addRendererPlugin(new FieldPlugin($field))
             ->setRendererVar('class', null)
             ->setRendererVar('max_length', null)
             ->setRendererVar('size', null)
@@ -167,7 +164,8 @@ class FormFactory
             ->setFieldFactory($options['field_factory'])
             ->setValidationGroups($options['validation_groups'])
             ->setVirtual($options['virtual'])
-            ->setValidator($options['validator']);
+            ->setValidator($options['validator'])
+            ->addRendererPlugin(new FormPlugin($form));
     }
 
     public function getField($key, array $options = array())
@@ -309,8 +307,11 @@ class FormFactory
             'value' => '1',
         ), $options);
 
-        return $this->getField($key, $options)
+        $field = $this->getField($key, $options);
+
+        return $field
             ->setValueTransformer(new BooleanToStringTransformer())
+            ->addRendererPlugin(new CheckedPlugin($field))
             ->setRendererVar('value', $options['value']);
     }
 
@@ -325,6 +326,7 @@ class FormFactory
 
         return $field
             ->setValueTransformer(new BooleanToStringTransformer())
+            ->addRendererPlugin(new CheckedPlugin($field))
             ->addRendererPlugin(new ParentNamePlugin($field))
             ->setRendererVar('value', $options['value']);
     }

+ 8 - 23
src/Symfony/Component/Form/Renderer/DefaultRenderer.php

@@ -15,7 +15,7 @@ use Symfony\Component\Form\FieldInterface;
 use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
 use Symfony\Component\Form\Renderer\Plugin\PluginInterface;
 
-class DefaultRenderer implements RendererInterface, \ArrayAccess
+class DefaultRenderer implements RendererInterface
 {
     private $field;
 
@@ -90,6 +90,11 @@ class DefaultRenderer implements RendererInterface, \ArrayAccess
         return $this->vars[$name];
     }
 
+    public function getVars()
+    {
+        return $this->vars;
+    }
+
     public function getWidget(array $vars = array())
     {
         return $this->render('widget', $vars);
@@ -105,9 +110,9 @@ class DefaultRenderer implements RendererInterface, \ArrayAccess
         return $this->render('row', $vars);
     }
 
-    public function getHidden(array $vars = array())
+    public function getRest(array $vars = array())
     {
-        return $this->render('hidden', $vars);
+        return $this->render('rest', $vars);
     }
 
     /**
@@ -134,24 +139,4 @@ class DefaultRenderer implements RendererInterface, \ArrayAccess
             $vars
         ));
     }
-
-    public function offsetGet($name)
-    {
-        return $this->getVar($name);
-    }
-
-    public function offsetExists($name)
-    {
-        return $this->hasVar($name);
-    }
-
-    public function offsetSet($name, $value)
-    {
-        throw new \BadMethodCallException('Not supported');
-    }
-
-    public function offsetUnset($name)
-    {
-        throw new \BadMethodCallException('Not supported');
-    }
 }

+ 2 - 11
src/Symfony/Component/Form/Renderer/Plugin/ValuePlugin.php

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Renderer\Plugin;
 use Symfony\Component\Form\Renderer\RendererInterface;
 use Symfony\Component\Form\FieldInterface;
 
-class ValuePlugin implements PluginInterface
+class CheckedPlugin implements PluginInterface
 {
     private $field;
 
@@ -23,17 +23,8 @@ class ValuePlugin implements PluginInterface
         $this->field = $field;
     }
 
-    /**
-     * Renders the HTML enctype in the field tag, if necessary
-     *
-     * Example usage in Twig templates:
-     *
-     *     <field action="..." method="post" {{ field.render.enctype }}>
-     *
-     * @param Form $field   The field for which to render the encoding type
-     */
     public function setUp(RendererInterface $renderer)
     {
-        $renderer->setVar('value', $this->field->getDisplayedData());
+        $renderer->setVar('checked', (bool)$this->field->getData());
     }
 }

+ 15 - 1
src/Symfony/Component/Form/Renderer/Plugin/NamePlugin.php

@@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Renderer\Plugin;
 use Symfony\Component\Form\Renderer\RendererInterface;
 use Symfony\Component\Form\FieldInterface;
 
-class NamePlugin implements PluginInterface
+class FieldPlugin implements PluginInterface
 {
     private $field;
 
@@ -38,12 +38,26 @@ class NamePlugin implements PluginInterface
 
         if ($this->field->hasParent()) {
             $parentRenderer = $this->field->getParent()->getRenderer();
+            $parentId = $parentRenderer->getVar('id');
             $parentName = $parentRenderer->getVar('name');
+            $id = sprintf('%s_%s', $parentId, $fieldKey);
             $name = sprintf('%s[%s]', $parentName, $fieldKey);
         } else {
+            $id = $fieldKey;
             $name = $fieldKey;
         }
 
+        $renderer->setVar('this', $renderer);
+        $renderer->setVar('id', $id);
         $renderer->setVar('name', $name);
+        $renderer->setVar('errors', $this->field->getErrors());
+        $renderer->setVar('value', $this->field->getDisplayedData());
+        $renderer->setVar('hidden', $this->field->isHidden());
+        $renderer->setVar('disabled', $this->field->isDisabled());
+        $renderer->setVar('required', $this->field->isRequired());
+        $renderer->setVar('class', null);
+        $renderer->setVar('max_length', null);
+        $renderer->setVar('size', null);
+        $renderer->setVar('label', ucfirst(strtolower(str_replace('_', ' ', $this->field->getKey()))));
     }
 }

+ 42 - 0
src/Symfony/Component/Form/Renderer/Plugin/FormPlugin.php

@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license infieldation, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Renderer\Plugin;
+
+use Symfony\Component\Form\Renderer\RendererInterface;
+use Symfony\Component\Form\FormInterface;
+
+class FormPlugin implements PluginInterface
+{
+    private $form;
+
+    public function __construct(FormInterface $form)
+    {
+        $this->form = $form;
+    }
+
+    public function setUp(RendererInterface $renderer)
+    {
+        $fields = array();
+        $visibleFields = array();
+
+        foreach ($this->form as $key => $field) {
+            $fields[$key] = $field->getRenderer();
+
+            if (!$field->isHidden()) {
+                $visibleFields[$key] = $field->getRenderer();
+            }
+        }
+
+        $renderer->setVar('fields', $fields);
+        $renderer->setVar('visible_fields', $visibleFields);
+    }
+}

+ 0 - 49
src/Symfony/Component/Form/Renderer/Plugin/IdPlugin.php

@@ -1,49 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
- *
- * For the full copyright and license infieldation, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Form\Renderer\Plugin;
-
-use Symfony\Component\Form\Renderer\RendererInterface;
-use Symfony\Component\Form\FieldInterface;
-
-class IdPlugin implements PluginInterface
-{
-    private $field;
-
-    public function __construct(FieldInterface $field)
-    {
-        $this->field = $field;
-    }
-
-    /**
-     * Renders the HTML enctype in the field tag, if necessary
-     *
-     * Example usage in Twig templates:
-     *
-     *     <field action="..." method="post" {{ field.render.enctype }}>
-     *
-     * @param Form $field   The field for which to render the encoding type
-     */
-    public function setUp(RendererInterface $renderer)
-    {
-        $fieldKey = $this->field->getKey();
-
-        if ($this->field->hasParent()) {
-            $parentRenderer = $this->field->getParent()->getRenderer();
-            $parentId = $parentRenderer->getVar('id');
-            $id = sprintf('%s_%s', $parentId, $fieldKey);
-        } else {
-            $id = $fieldKey;
-        }
-
-        $renderer->setVar('id', $id);
-    }
-}

+ 1 - 4
src/Symfony/Component/Form/Renderer/Plugin/SelectMultipleNamePlugin.php

@@ -12,14 +12,11 @@
 namespace Symfony\Component\Form\Renderer\Plugin;
 
 use Symfony\Component\Form\Renderer\RendererInterface;
-use Symfony\Component\Form\FieldInterface;
 
-class SelectMultipleNamePlugin extends NamePlugin
+class SelectMultipleNamePlugin implements PluginInterface
 {
     public function setUp(RendererInterface $renderer)
     {
-        parent::setUp($renderer);
-
         // Add "[]" to the name in case a select tag with multiple options is
         // displayed. Otherwise only one of the selected options is sent in the
         // POST request.

+ 1 - 1
src/Symfony/Component/Form/Renderer/RendererInterface.php

@@ -23,7 +23,7 @@ interface RendererInterface
 
     function getRow(array $vars = array());
 
-    function getHidden(array $vars = array());
+    function getRest(array $vars = array());
 
     /**
      * Renders the label of the given field

+ 0 - 18
tests/Symfony/Tests/Component/Form/FormTest.php

@@ -1103,24 +1103,6 @@ class FormTest extends TestCase
         $this->assertEquals(array('firstName' => 'Bernhard'), $form->getData());
     }
 
-    public function testGetHiddenFieldsReturnsOnlyHiddenFields()
-    {
-        $form = $this->getGroupWithBothVisibleAndHiddenField();
-
-        $hiddenFields = $form->getHiddenFields(true, false);
-
-        $this->assertSame(array($form['hiddenField']), $hiddenFields);
-    }
-
-    public function testGetVisibleFieldsReturnsOnlyVisibleFields()
-    {
-        $form = $this->getGroupWithBothVisibleAndHiddenField();
-
-        $visibleFields = $form->getVisibleFields(true, false);
-
-        $this->assertSame(array($form['visibleField']), $visibleFields);
-    }
-
     public function testValidateData()
     {
         $graphWalker = $this->createMockGraphWalker();