Selaa lähdekoodia

[Form] Moved properties propertyPath, modifyByReference, validationGroups and virtual to generic attributes because they are specific to the used data validator/mapper implementations

Bernhard Schussek 14 vuotta sitten
vanhempi
commit
97d0183d84

+ 4 - 4
src/Symfony/Component/Form/Config/DateFieldConfig.php

@@ -64,10 +64,7 @@ class DateFieldConfig extends AbstractFieldConfig
                     'output_timezone' => $options['user_timezone'],
                     'fields' => array('year', 'month', 'day'),
                 )))
-                ->addRendererPlugin(new DatePatternPlugin($formatter))
-                // Don't modify \DateTime classes by reference, we treat
-                // them like immutable value objects
-                ->setModifyByReference(false);
+                ->addRendererPlugin(new DatePatternPlugin($formatter));
         }
 
         if ($options['type'] === 'string') {
@@ -112,6 +109,9 @@ class DateFieldConfig extends AbstractFieldConfig
             'data_timezone' => date_default_timezone_get(),
             'user_timezone' => date_default_timezone_get(),
             'csrf_protection' => false,
+            // Don't modify \DateTime classes by reference, we treat
+            // them like immutable value objects
+            'by_reference' => false,
         );
     }
 

+ 4 - 5
src/Symfony/Component/Form/Config/DateTimeFieldConfig.php

@@ -80,11 +80,7 @@ class DateTimeFieldConfig extends AbstractFieldConfig
                 )),
             )))
             ->add('date', 'date', $dateOptions)
-            ->add('time', 'time', $timeOptions)
-            // Don't modify \DateTime classes by reference, we treat
-            // them like immutable value objects
-            ->setModifyByReference(false)
-            ->setData(null); // hack: should be invoked automatically
+            ->add('time', 'time', $timeOptions);
 
         if ($options['type'] == 'string') {
             $builder->setNormalizationTransformer(new ReversedTransformer(
@@ -120,6 +116,9 @@ class DateTimeFieldConfig extends AbstractFieldConfig
             'with_seconds' => false,
             'data_timezone' => date_default_timezone_get(),
             'user_timezone' => date_default_timezone_get(),
+            // Don't modify \DateTime classes by reference, we treat
+            // them like immutable value objects
+            'by_reference' => false,
         );
     }
 

+ 22 - 3
src/Symfony/Component/Form/Config/FieldConfig.php

@@ -12,6 +12,7 @@
 namespace Symfony\Component\Form\Config;
 
 use Symfony\Component\Form\Field;
+use Symfony\Component\Form\PropertyPath;
 use Symfony\Component\Form\FieldBuilder;
 use Symfony\Component\Form\FormFactoryInterface;
 use Symfony\Component\Form\Renderer\DefaultRenderer;
@@ -41,9 +42,25 @@ class FieldConfig extends AbstractFieldConfig
 
     public function configure(FieldBuilder $builder, array $options)
     {
-        $builder->setPropertyPath($options['property_path'])
-            ->setRequired($options['required'])
+        if (false === $options['property_path']) {
+            $options['property_path'] = $builder->getName();
+        }
+
+        if (null === $options['property_path'] || '' === $options['property_path']) {
+            $options['property_path'] = null;
+        } else {
+            $options['property_path'] = new PropertyPath($options['property_path']);
+        }
+
+        $options['validation_groups'] = empty($options['validation_groups'])
+            ? null
+            : (array)$options['validation_groups'];
+
+        $builder->setRequired($options['required'])
             ->setDisabled($options['disabled'])
+            ->setAttribute('by_reference', $options['by_reference'])
+            ->setAttribute('property_path', $options['property_path'])
+            ->setAttribute('validation_groups', $options['validation_groups'])
             ->setValueTransformer($options['value_transformer'])
             ->setNormalizationTransformer($options['normalization_transformer'])
             ->addEventSubscriber(new ValidationListener($this->validator), -128)
@@ -61,13 +78,15 @@ class FieldConfig extends AbstractFieldConfig
         return array(
             'template' => 'text',
             'data' => null,
-            'property_path' => false,
             'trim' => true,
             'required' => true,
             'disabled' => false,
             'value_transformer' => null,
             'normalization_transformer' => null,
             'max_length' => null,
+            'property_path' => false,
+            'by_reference' => true,
+            'validation_groups' => true,
         );
     }
 

+ 1 - 2
src/Symfony/Component/Form/Config/FormConfig.php

@@ -21,8 +21,7 @@ class FormConfig extends AbstractFieldConfig
 {
     public function configure(FieldBuilder $builder, array $options)
     {
-        $builder->setValidationGroups($options['validation_groups'])
-            ->setVirtual($options['virtual'])
+        $builder->setAttribute('virtual', $options['virtual'])
             ->addRendererPlugin(new FormPlugin())
             ->setDataClass($options['data_class'])
             ->setDataMapper(new ObjectMapper(

+ 4 - 4
src/Symfony/Component/Form/Config/TimeFieldConfig.php

@@ -42,10 +42,7 @@ class TimeFieldConfig extends AbstractFieldConfig
         }
 
         $builder->add($options['widget'], 'hour', $hourOptions)
-            ->add($options['widget'], 'minute', $minuteOptions)
-            // Don't modify \DateTime classes by reference, we treat
-            // them like immutable value objects
-            ->setModifyByReference(false);
+            ->add($options['widget'], 'minute', $minuteOptions);
 
         if ($options['with_seconds']) {
             $parts[] = 'second';
@@ -104,6 +101,9 @@ class TimeFieldConfig extends AbstractFieldConfig
             'data_timezone' => date_default_timezone_get(),
             'user_timezone' => date_default_timezone_get(),
             'csrf_protection' => false,
+            // Don't modify \DateTime classes by reference, we treat
+            // them like immutable value objects
+            'by_reference' => false,
         );
     }
 

+ 7 - 7
src/Symfony/Component/Form/DataMapper/ObjectMapper.php

@@ -73,8 +73,8 @@ class ObjectMapper implements DataMapperInterface
 
     public function mapDataToField(&$data, FieldInterface $field)
     {
-        if ($field->getPropertyPath() !== null) {
-            $field->setData($field->getPropertyPath()->getValue($data));
+        if ($field->getAttribute('property_path') !== null) {
+            $field->setData($field->getAttribute('property_path')->getValue($data));
         }
     }
 
@@ -88,14 +88,14 @@ class ObjectMapper implements DataMapperInterface
 
             // If the data is identical to the value in $data, we are
             // dealing with a reference
-            if ($field->getPropertyPath() !== null) {
-                $isReference = $field->getData() === $field->getPropertyPath()->getValue($data);
+            if ($field->getAttribute('property_path') !== null) {
+                $isReference = $field->getData() === $field->getAttribute('property_path')->getValue($data);
             }
 
             // Don't write into $data if $data is an object,
             // $isReference is true (see above) and the option "by_reference" is
             // true as well
-            if (!is_object($data) || !$isReference || !$field->isModifiedByReference()) {
+            if (!is_object($data) || !$isReference || !$field->getAttribute('by_reference')) {
                 $this->mapFieldToData($field, $data);
             }
         }
@@ -103,8 +103,8 @@ class ObjectMapper implements DataMapperInterface
 
     public function mapFieldToData(FieldInterface $field, &$data)
     {
-        if ($field->getPropertyPath() !== null) {
-            $field->getPropertyPath()->setValue($data, $field->getData());
+        if ($field->getAttribute('property_path') !== null) {
+            $field->getAttribute('property_path')->setValue($data, $field->getData());
         }
     }
 }

+ 1 - 1
src/Symfony/Component/Form/EventListener/ValidationListener.php

@@ -91,7 +91,7 @@ class ValidationListener implements EventSubscriberInterface
                 $iterator = new \RecursiveIteratorIterator($iterator);
 
                 foreach ($iterator as $child) {
-                    if (null !== ($childPath = $child->getPropertyPath())) {
+                    if (null !== ($childPath = $child->getAttribute('property_path'))) {
                         if ($childPath->getElement(0) === $pathIterator->current()) {
                             if ($pathIterator->hasNext()) {
                                 $pathIterator->next();

+ 22 - 50
src/Symfony/Component/Form/Field.php

@@ -64,34 +64,25 @@ class Field implements FieldInterface
     private $transformedData = '';
     private $normalizationTransformer;
     private $valueTransformer;
-    private $propertyPath;
     private $transformationSuccessful = true;
     private $renderer;
     private $disabled = false;
     private $dispatcher;
-    private $modifyByReference = true;
-    private $validationGroups;
+    private $attributes;
 
     public function __construct($name, EventDispatcherInterface $dispatcher,
         RendererInterface $renderer, ValueTransformerInterface $valueTransformer = null,
         ValueTransformerInterface $normalizationTransformer = null,
-        $disabled, $modifyByReference, $propertyPath, $required,
-        $validationGroups)
+        $required = false, $disabled = false, array $attributes = array())
     {
         $this->name = (string)$name;
         $this->dispatcher = $dispatcher;
         $this->renderer = $renderer;
         $this->valueTransformer = $valueTransformer;
         $this->normalizationTransformer = $normalizationTransformer;
-        $this->disabled = $disabled;
-        $this->modifyByReference = $modifyByReference;
-        $this->propertyPath = null === $propertyPath || '' === $propertyPath
-                ? null
-                : new PropertyPath($propertyPath);
         $this->required = $required;
-        $this->validationGroups = empty($validationGroups)
-                ? null
-                : (array)$validationGroups;
+        $this->disabled = $disabled;
+        $this->attributes = $attributes;
 
         $renderer->setField($this);
 
@@ -115,14 +106,6 @@ class Field implements FieldInterface
         return $this->transformedData;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public function getPropertyPath()
-    {
-        return $this->propertyPath;
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -205,6 +188,16 @@ class Field implements FieldInterface
         return !$this->hasParent();
     }
 
+    public function hasAttribute($name)
+    {
+        return isset($this->attributes[$name]);
+    }
+
+    public function getAttribute($name)
+    {
+        return $this->attributes[$name];
+    }
+
     /**
      * Updates the field with default data
      *
@@ -343,6 +336,14 @@ class Field implements FieldInterface
         return $this->transformationSuccessful;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function isEmpty()
+    {
+        return null === $this->data || '' === $this->data;
+    }
+
     /**
      * Returns whether the field is valid.
      *
@@ -464,33 +465,4 @@ class Field implements FieldInterface
         }
         return $this->valueTransformer->reverseTransform($value, $this->data);
     }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function isEmpty()
-    {
-        return null === $this->data || '' === $this->data;
-    }
-
-    public function isModifiedByReference()
-    {
-        return $this->modifyByReference;
-    }
-
-    /**
-     * Returns the validation groups validated by the form
-     *
-     * @return array  A list of validation groups or null
-     */
-    public function getValidationGroups()
-    {
-        $groups = $this->validationGroups;
-
-        if (!$groups && $this->hasParent()) {
-            $groups = $this->getParent()->getValidationGroups();
-        }
-
-        return $groups;
-    }
 }

+ 28 - 77
src/Symfony/Component/Form/FieldBuilder.php

@@ -37,14 +37,8 @@ class FieldBuilder
 
     private $csrfProvider;
 
-    private $validationGroups;
-
-    private $modifyByReference = true;
-
     private $disabled;
 
-    private $propertyPath = false;
-
     private $required;
 
     private $renderer;
@@ -57,12 +51,12 @@ class FieldBuilder
 
     private $theme;
 
-    private $virtual = false;
-
     private $fields = array();
 
     private $dataMapper;
 
+    private $attributes = array();
+
     public function __construct(ThemeInterface $theme,
             EventDispatcherInterface $dispatcher,
             CsrfProviderInterface $csrfProvider)
@@ -108,30 +102,6 @@ class FieldBuilder
         return $this->data;
     }
 
-    public function setValidationGroups($validationGroups)
-    {
-        $this->validationGroups = $validationGroups;
-
-        return $this;
-    }
-
-    public function getValidationGroups()
-    {
-        return $this->validationGroups;
-    }
-
-    public function setModifyByReference($modifyByReference)
-    {
-        $this->modifyByReference = $modifyByReference;
-
-        return $this;
-    }
-
-    public function getModifyByReference()
-    {
-        return $this->modifyByReference;
-    }
-
     public function setDisabled($disabled)
     {
         $this->disabled = $disabled;
@@ -144,26 +114,6 @@ class FieldBuilder
         return $this->disabled;
     }
 
-    /**
-     * Sets the property path
-     *
-     * The property path determines the property or a sequence of properties
-     * that a field updates in the data of the form.
-     *
-     * @param string $propertyPath
-     */
-    public function setPropertyPath($propertyPath)
-    {
-        $this->propertyPath = $propertyPath;
-
-        return $this;
-    }
-
-    public function getPropertyPath()
-    {
-        return $this->propertyPath;
-    }
-
     /**
      * Sets whether this field is required to be filled out when bound.
      *
@@ -181,18 +131,6 @@ class FieldBuilder
         return $this->required;
     }
 
-    public function setVirtual($virtual)
-    {
-        $this->virtual = $virtual;
-
-        return $this;
-    }
-
-    public function getVirtual()
-    {
-        return $this->virtual;
-    }
-
     public function setDataMapper(DataMapperInterface $dataMapper)
     {
         $this->dataMapper = $dataMapper;
@@ -510,6 +448,28 @@ class FieldBuilder
         return $this->dataClass;
     }
 
+    public function setAttribute($name, $value)
+    {
+        $this->attributes[$name] = $value;
+
+        return $this;
+    }
+
+    public function getAttribute($name)
+    {
+        return $this->attributes[$name];
+    }
+
+    public function hasAttribute($name)
+    {
+        return isset($this->attributes[$name]);
+    }
+
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
     public function getInstance()
     {
         $this->buildCsrfProtection();
@@ -522,14 +482,9 @@ class FieldBuilder
                 $this->getValueTransformer(),
                 $this->getNormalizationTransformer(),
                 $this->getDataMapper(),
-                $this->getDisabled(),
-                $this->getModifyByReference(),
-                $this->getPropertyPath() === false
-                    ? $this->name
-                    : $this->getPropertyPath(),
                 $this->getRequired(),
-                $this->getValidationGroups(),
-                $this->getVirtual()
+                $this->getDisabled(),
+                $this->getAttributes()
             );
 
             foreach ($this->buildFields() as $field) {
@@ -542,13 +497,9 @@ class FieldBuilder
                 $this->buildRenderer(),
                 $this->getValueTransformer(),
                 $this->getNormalizationTransformer(),
-                $this->getDisabled(),
-                $this->getModifyByReference(),
-                $this->getPropertyPath() === false
-                    ? $this->name
-                    : $this->getPropertyPath(),
                 $this->getRequired(),
-                $this->getValidationGroups()
+                $this->getDisabled(),
+                $this->getAttributes()
             );
         }
 

+ 0 - 7
src/Symfony/Component/Form/FieldInterface.php

@@ -39,13 +39,6 @@ interface FieldInterface
      */
     function getName();
 
-    /**
-     * Returns the property path of the field
-     *
-     * @return PropertyPath
-     */
-    function getPropertyPath();
-
     /**
      * Adds an error to this field
      *

+ 13 - 17
src/Symfony/Component/Form/Form.php

@@ -66,9 +66,8 @@ class Form extends Field implements \IteratorAggregate, FormInterface
     public function __construct($name, EventDispatcherInterface $dispatcher,
         RendererInterface $renderer, ValueTransformerInterface $valueTransformer = null,
         ValueTransformerInterface $normalizationTransformer = null,
-        DataMapperInterface $dataMapper,
-        $disabled, $modifyByReference, $propertyPath, $required,
-        $validationGroups, $virtual)
+        DataMapperInterface $dataMapper, $required = false, $disabled = false,
+        array $attributes = array())
     {
         $dispatcher->addListener(array(
             Events::postSetData,
@@ -77,12 +76,10 @@ class Form extends Field implements \IteratorAggregate, FormInterface
             Events::filterBoundDataFromClient,
         ), $this);
 
-        $this->virtual = $virtual;
         $this->dataMapper = $dataMapper;
 
         parent::__construct($name, $dispatcher, $renderer, $valueTransformer,
-            $normalizationTransformer, $disabled, $modifyByReference,
-            $propertyPath, $required, $validationGroups);
+            $normalizationTransformer, $required, $disabled, $attributes);
     }
 
     /**
@@ -202,14 +199,6 @@ class Form extends Field implements \IteratorAggregate, FormInterface
         }
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function isVirtual()
-    {
-        return $this->virtual;
-    }
-
     /**
      * Returns whether this form was bound with extra fields
      *
@@ -397,14 +386,21 @@ class Form extends Field implements \IteratorAggregate, FormInterface
     public function validateData(ExecutionContext $context)
     {
         if (is_object($this->getData()) || is_array($this->getData())) {
-            $groups = $this->getValidationGroups();
-            $propertyPath = $context->getPropertyPath();
-            $graphWalker = $context->getGraphWalker();
+            $groups = $this->getAttribute('validation_groups');
+            $field = $this;
+
+            while (!$groups && $field->hasParent()) {
+                $field = $field->getParent();
+                $groups = $field->getAttribute('validation_groups');
+            }
 
             if (null === $groups) {
                 $groups = array(null);
             }
 
+            $propertyPath = $context->getPropertyPath();
+            $graphWalker = $context->getGraphWalker();
+
             // The Execute constraint is called on class level, so we need to
             // set the property manually
             $context->setCurrentProperty('data');

+ 1 - 24
src/Symfony/Component/Form/FormInterface.php

@@ -14,31 +14,8 @@ namespace Symfony\Component\Form;
 /**
  * A field group bundling multiple form fields
  *
- * @author     Bernhard Schussek <bernhard.schussek@symfony.com>
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
  */
 interface FormInterface extends FieldInterface, \ArrayAccess, \Traversable, \Countable
 {
-    /**
-     * Returns whether this field group is virtual
-     *
-     * Virtual field groups are skipped when mapping property paths of a form
-     * tree to an object.
-     *
-     * Example:
-     *
-     * <code>
-     * $group = new Form('address');
-     * $group->add(new TextField('street'));
-     * $group->add(new TextField('postal_code'));
-     * $form->add($group);
-     * </code>
-     *
-     * If $group is non-virtual, the fields "street" and "postal_code"
-     * are mapped to the property paths "address.street" and
-     * "address.postal_code". If $group is virtual though, the fields are
-     * mapped directly to "street" and "postal_code".
-     *
-     * @return Boolean  Whether the group is virtual
-     */
-    public function isVirtual();
 }

+ 1 - 1
src/Symfony/Component/Form/RecursiveFieldIterator.php

@@ -34,6 +34,6 @@ class RecursiveFieldIterator extends \IteratorIterator implements \RecursiveIter
     public function hasChildren()
     {
         return $this->current() instanceof FormInterface
-                && $this->current()->isVirtual();
+                && $this->current()->getAttribute('virtual');
     }
 }

+ 4 - 4
tests/Symfony/Tests/Component/Form/FieldTest.php

@@ -42,28 +42,28 @@ class FieldTest extends TestCase
     {
         $field = $this->factory->create('field', 'title');
 
-        $this->assertEquals(new PropertyPath('title'), $field->getPropertyPath());
+        $this->assertEquals(new PropertyPath('title'), $field->getAttribute('property_path'));
     }
 
     public function testGetPropertyPath_pathIsZero()
     {
         $field = $this->factory->create('field', 'title', array('property_path' => '0'));
 
-        $this->assertEquals(new PropertyPath('0'), $field->getPropertyPath());
+        $this->assertEquals(new PropertyPath('0'), $field->getAttribute('property_path'));
     }
 
     public function testGetPropertyPath_pathIsEmpty()
     {
         $field = $this->factory->create('field', 'title', array('property_path' => ''));
 
-        $this->assertEquals(null, $field->getPropertyPath());
+        $this->assertEquals(null, $field->getAttribute('property_path'));
     }
 
     public function testGetPropertyPath_pathIsNull()
     {
         $field = $this->factory->create('field', 'title', array('property_path' => null));
 
-        $this->assertEquals(null, $field->getPropertyPath());
+        $this->assertEquals(null, $field->getAttribute('property_path'));
     }
 
     public function testPassRequiredAsOption()

+ 3 - 27
tests/Symfony/Tests/Component/Form/FormTest.php

@@ -215,7 +215,7 @@ class FormTest extends TestCase
 
     public function testValidationGroupNullByDefault()
     {
-        $this->assertNull($this->form->getValidationGroups());
+        $this->assertNull($this->form->getAttribute('validation_groups'));
     }
 
     public function testValidationGroupsCanBeSetToString()
@@ -224,7 +224,7 @@ class FormTest extends TestCase
             'validation_groups' => 'group',
         ));
 
-        $this->assertEquals(array('group'), $form->getValidationGroups());
+        $this->assertEquals(array('group'), $form->getAttribute('validation_groups'));
     }
 
     public function testValidationGroupsCanBeSetToArray()
@@ -233,31 +233,7 @@ class FormTest extends TestCase
             'validation_groups' => array('group1', 'group2'),
         ));
 
-        $this->assertEquals(array('group1', 'group2'), $form->getValidationGroups());
-    }
-
-    public function testValidationGroupsAreInheritedFromParentIfEmpty()
-    {
-        $builder = $this->factory->createBuilder('form', 'parent', array(
-            'validation_groups' => 'group',
-        ));
-        $builder->add('form', 'child');
-        $form = $builder->getInstance();
-
-        $this->assertEquals(array('group'), $form['child']->getValidationGroups());
-    }
-
-    public function testValidationGroupsAreNotInheritedFromParentIfSet()
-    {
-        $builder = $this->factory->createBuilder('form', 'parent', array(
-            'validation_groups' => 'group1',
-        ));
-        $builder->add('form', 'child', array(
-            'validation_groups' => 'group2',
-        ));
-        $form = $builder->getInstance();
-
-        $this->assertEquals(array('group2'), $form['child']->getValidationGroups());
+        $this->assertEquals(array('group1', 'group2'), $form->getAttribute('validation_groups'));
     }
 
     public function testBindValidatesData()