Procházet zdrojové kódy

[Form] Split FieldBuilder into FieldBuilder and FormBuilder to make creation of classes deterministic

Bernhard Schussek před 14 roky
rodič
revize
1c85daa56b

+ 14 - 259
src/Symfony/Component/Form/FieldBuilder.php

@@ -11,14 +11,12 @@
 
 namespace Symfony\Component\Form;
 
-use Symfony\Component\Form\DataMapper\DataMapperInterface;
 use Symfony\Component\Form\DataValidator\DataValidatorInterface;
 use Symfony\Component\Form\DataTransformer\DataTransformerInterface;
 use Symfony\Component\Form\Renderer\DefaultRenderer;
 use Symfony\Component\Form\Renderer\RendererInterface;
 use Symfony\Component\Form\Renderer\Plugin\RendererPluginInterface;
 use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
-use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
@@ -28,16 +26,10 @@ class FieldBuilder
 
     private $data;
 
-    private $dataClass;
-
     private $dispatcher;
 
     private $factory;
 
-    private $csrfFieldName;
-
-    private $csrfProvider;
-
     private $disabled;
 
     private $required;
@@ -52,21 +44,15 @@ class FieldBuilder
 
     private $theme;
 
-    private $fields = array();
-
-    private $dataMapper;
-
     private $dataValidator;
 
     private $attributes = array();
 
     public function __construct(ThemeInterface $theme,
-            EventDispatcherInterface $dispatcher,
-            CsrfProviderInterface $csrfProvider)
+            EventDispatcherInterface $dispatcher)
     {
         $this->theme = $theme;
         $this->dispatcher = $dispatcher;
-        $this->csrfProvider = $csrfProvider;
     }
 
     public function setFormFactory(FormFactoryInterface $factory)
@@ -134,16 +120,6 @@ class FieldBuilder
         return $this->required;
     }
 
-    public function setDataMapper(DataMapperInterface $dataMapper)
-    {
-        $this->dataMapper = $dataMapper;
-    }
-
-    public function getDataMapper()
-    {
-        return $this->dataMapper;
-    }
-
     public function setDataValidator(DataValidatorInterface $dataValidator)
     {
         $this->dataValidator = $dataValidator;
@@ -154,194 +130,6 @@ class FieldBuilder
         return $this->dataValidator;
     }
 
-    /**
-     * Adds a new field to this group. A field must have a unique name within
-     * the group. Otherwise the existing field is overwritten.
-     *
-     * If you add a nested group, this group should also be represented in the
-     * object hierarchy. If you want to add a group that operates on the same
-     * hierarchy level, use merge().
-     *
-     * <code>
-     * class Entity
-     * {
-     *   public $location;
-     * }
-     *
-     * class Location
-     * {
-     *   public $longitude;
-     *   public $latitude;
-     * }
-     *
-     * $entity = new Entity();
-     * $entity->location = new Location();
-     *
-     * $form = new Form('entity', $entity, $validator);
-     *
-     * $locationGroup = new Form('location');
-     * $locationGroup->add(new TextField('longitude'));
-     * $locationGroup->add(new TextField('latitude'));
-     *
-     * $form->add($locationGroup);
-     * </code>
-     *
-     * @param FieldInterface|string $field
-     * @return FieldInterface
-     */
-    public function add($field)
-    {
-        // if the field is given as string, ask the field factory of the form
-        // to create a field
-        if ($field instanceof FieldInterface) {
-            $this->fields[$field->getName()] = $field;
-
-            return $this;
-        }
-
-        if (!is_string($field)) {
-            throw new UnexpectedTypeException($field, 'FieldInterface or string');
-        }
-
-        // TODO turn order of $type and $name around
-
-        if (func_num_args() > 2 || (func_num_args() > 1 && !is_array(func_get_arg(1)))) {
-            $type = func_get_arg(0);
-            $name = func_get_arg(1);
-            $options = func_num_args() > 2 ? func_get_arg(2) : array();
-
-            $this->fields[$name] = array(
-                'type' => $type,
-                'options' => $options,
-            );
-
-            return $this;
-        }
-
-        if (!$this->dataClass) {
-            throw new FormException('The data class must be set to automatically create fields');
-        }
-
-        $property = func_get_arg(0);
-        $options = func_num_args() > 1 ? func_get_arg(1) : array();
-
-        $this->fields[$property] = array(
-            'class' => $this->dataClass,
-            'options' => $options,
-        );
-
-        return $this;
-    }
-
-    public function get($name)
-    {
-        if (!isset($this->fields[$name])) {
-            throw new FormException(sprintf('The field "%s" does not exist', $name));
-        }
-
-        if ($this->fields[$name] instanceof FieldBuilder) {
-            return $this->fields[$name];
-        }
-
-        if (isset($this->fields[$name]['type'])) {
-            $this->fields[$name] = $this->factory->createBuilder(
-                $this->fields[$name]['type'],
-                $name,
-                $this->fields[$name]['options']
-            );
-
-            return $this->fields[$name];
-        }
-
-        $this->fields[$name] = $this->factory->createBuilderForProperty(
-            $this->fields[$name]['class'],
-            $name,
-            $this->fields[$name]['options']
-        );
-
-        return $this->fields[$name];
-    }
-
-    protected function buildFields()
-    {
-        $fields = array();
-
-        foreach ($this->fields as $name => $field) {
-            $fields[$name] = $this->get($name)->getInstance();
-        }
-
-        return $fields;
-    }
-
-    /**
-     * Removes the field with the given name.
-     *
-     * @param string $name
-     */
-    public function remove($name)
-    {
-        $this->fields[$name]->setParent(null);
-
-        unset($this->fields[$name]);
-    }
-
-    /**
-     * Returns whether a field with the given name exists.
-     *
-     * @param  string $name
-     * @return Boolean
-     */
-    public function has($name)
-    {
-        return isset($this->fields[$name]);
-    }
-
-    public function addCsrfProtection(CsrfProviderInterface $provider = null, $fieldName = '_token')
-    {
-        if (null !== $provider) {
-            $this->csrfProvider = $provider;
-        }
-
-        $this->csrfFieldName = $fieldName;
-    }
-
-    public function removeCsrfProtection()
-    {
-        $this->csrfFieldName = null;
-
-        return $this;
-    }
-
-    /**
-     * @return true if this form is CSRF protected
-     */
-    public function hasCsrfProtection()
-    {
-        return isset($this->csrfFieldName);
-    }
-
-    public function getCsrfFieldName()
-    {
-        return $this->csrfFieldName;
-    }
-
-    public function getCsrfProvider()
-    {
-        return $this->csrfProvider;
-    }
-
-    protected function buildCsrfProtection()
-    {
-        if ($this->hasCsrfProtection()) {
-            $token = $this->csrfProvider->generateCsrfToken(get_class($this));
-
-            $this->add('hidden', $this->csrfFieldName, array(
-                'data' => $token,
-                'property_path' => null,
-            ));
-        }
-    }
-
     /**
      * Adds an event listener for events on this field
      *
@@ -449,18 +237,6 @@ class FieldBuilder
         return $this->renderer;
     }
 
-    public function setDataClass($class)
-    {
-        $this->dataClass = $class;
-
-        return $this;
-    }
-
-    public function getDataClass()
-    {
-        return $this->dataClass;
-    }
-
     public function setAttribute($name, $value)
     {
         $this->attributes[$name] = $value;
@@ -485,41 +261,20 @@ class FieldBuilder
 
     public function getInstance()
     {
-        $this->buildCsrfProtection();
-
-        if (count($this->fields) > 0) {
-            $instance = new Form(
-                $this->name,
-                $this->buildDispatcher(),
-                $this->buildRenderer(),
-                $this->getDataTransformer(),
-                $this->getNormalizationTransformer(),
-                $this->getDataMapper(),
-                $this->getDataValidator(),
-                $this->getRequired(),
-                $this->getDisabled(),
-                $this->getAttributes()
-            );
-
-            foreach ($this->buildFields() as $field) {
-                $instance->add($field);
-            }
-        } else {
-            $instance = new Field(
-                $this->name,
-                $this->buildDispatcher(),
-                $this->buildRenderer(),
-                $this->getDataTransformer(),
-                $this->getNormalizationTransformer(),
-                $this->getDataValidator(),
-                $this->getRequired(),
-                $this->getDisabled(),
-                $this->getAttributes()
-            );
-        }
+        $instance = new Field(
+            $this->getName(),
+            $this->buildDispatcher(),
+            $this->buildRenderer(),
+            $this->getDataTransformer(),
+            $this->getNormalizationTransformer(),
+            $this->getDataValidator(),
+            $this->getRequired(),
+            $this->getDisabled(),
+            $this->getAttributes()
+        );
 
-        if ($this->data) {
-            $instance->setData($this->data);
+        if ($this->getData()) {
+            $instance->setData($this->getData());
         }
 
         return $instance;

+ 277 - 0
src/Symfony/Component/Form/FormBuilder.php

@@ -0,0 +1,277 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form;
+
+use Symfony\Component\Form\DataMapper\DataMapperInterface;
+use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
+use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+class FormBuilder extends FieldBuilder
+{
+    private $dataClass;
+
+    private $csrfFieldName;
+
+    private $csrfProvider;
+
+    private $fields = array();
+
+    private $dataMapper;
+
+    public function __construct(ThemeInterface $theme,
+            EventDispatcherInterface $dispatcher,
+            CsrfProviderInterface $csrfProvider)
+    {
+        parent::__construct($theme, $dispatcher);
+
+        $this->csrfProvider = $csrfProvider;
+    }
+
+    public function setDataMapper(DataMapperInterface $dataMapper)
+    {
+        $this->dataMapper = $dataMapper;
+    }
+
+    public function getDataMapper()
+    {
+        return $this->dataMapper;
+    }
+
+    /**
+     * Adds a new field to this group. A field must have a unique name within
+     * the group. Otherwise the existing field is overwritten.
+     *
+     * If you add a nested group, this group should also be represented in the
+     * object hierarchy. If you want to add a group that operates on the same
+     * hierarchy level, use merge().
+     *
+     * <code>
+     * class Entity
+     * {
+     *   public $location;
+     * }
+     *
+     * class Location
+     * {
+     *   public $longitude;
+     *   public $latitude;
+     * }
+     *
+     * $entity = new Entity();
+     * $entity->location = new Location();
+     *
+     * $form = new Form('entity', $entity, $validator);
+     *
+     * $locationGroup = new Form('location');
+     * $locationGroup->add(new TextField('longitude'));
+     * $locationGroup->add(new TextField('latitude'));
+     *
+     * $form->add($locationGroup);
+     * </code>
+     *
+     * @param FieldInterface|string $field
+     * @return FieldInterface
+     */
+    public function add($field)
+    {
+        // if the field is given as string, ask the field factory of the form
+        // to create a field
+        if ($field instanceof FieldInterface) {
+            $this->fields[$field->getName()] = $field;
+
+            return $this;
+        }
+
+        if (!is_string($field)) {
+            throw new UnexpectedTypeException($field, 'FieldInterface or string');
+        }
+
+        // TODO turn order of $type and $name around
+
+        if (func_num_args() > 2 || (func_num_args() > 1 && !is_array(func_get_arg(1)))) {
+            $type = func_get_arg(0);
+            $name = func_get_arg(1);
+            $options = func_num_args() > 2 ? func_get_arg(2) : array();
+
+            $this->fields[$name] = array(
+                'type' => $type,
+                'options' => $options,
+            );
+
+            return $this;
+        }
+
+        if (!$this->dataClass) {
+            throw new FormException('The data class must be set to automatically create fields');
+        }
+
+        $property = func_get_arg(0);
+        $options = func_num_args() > 1 ? func_get_arg(1) : array();
+
+        $this->fields[$property] = array(
+            'class' => $this->dataClass,
+            'options' => $options,
+        );
+
+        return $this;
+    }
+
+    public function get($name)
+    {
+        if (!isset($this->fields[$name])) {
+            throw new FormException(sprintf('The field "%s" does not exist', $name));
+        }
+
+        if ($this->fields[$name] instanceof FieldBuilder) {
+            return $this->fields[$name];
+        }
+
+        if (isset($this->fields[$name]['type'])) {
+            $this->fields[$name] = $this->getFormFactory()->createBuilder(
+                $this->fields[$name]['type'],
+                $name,
+                $this->fields[$name]['options']
+            );
+
+            return $this->fields[$name];
+        }
+
+        $this->fields[$name] = $this->getFormFactory()->createBuilderForProperty(
+            $this->fields[$name]['class'],
+            $name,
+            $this->fields[$name]['options']
+        );
+
+        return $this->fields[$name];
+    }
+
+    /**
+     * Removes the field with the given name.
+     *
+     * @param string $name
+     */
+    public function remove($name)
+    {
+        $this->fields[$name]->setParent(null);
+
+        unset($this->fields[$name]);
+    }
+
+    /**
+     * Returns whether a field with the given name exists.
+     *
+     * @param  string $name
+     * @return Boolean
+     */
+    public function has($name)
+    {
+        return isset($this->fields[$name]);
+    }
+
+    protected function buildFields()
+    {
+        $fields = array();
+
+        foreach ($this->fields as $name => $field) {
+            $fields[$name] = $this->get($name)->getInstance();
+        }
+
+        return $fields;
+    }
+
+    public function addCsrfProtection(CsrfProviderInterface $provider = null, $fieldName = '_token')
+    {
+        if (null !== $provider) {
+            $this->csrfProvider = $provider;
+        }
+
+        $this->csrfFieldName = $fieldName;
+    }
+
+    public function removeCsrfProtection()
+    {
+        $this->csrfFieldName = null;
+
+        return $this;
+    }
+
+    /**
+     * @return true if this form is CSRF protected
+     */
+    public function hasCsrfProtection()
+    {
+        return isset($this->csrfFieldName);
+    }
+
+    public function getCsrfFieldName()
+    {
+        return $this->csrfFieldName;
+    }
+
+    public function getCsrfProvider()
+    {
+        return $this->csrfProvider;
+    }
+
+    protected function buildCsrfProtection()
+    {
+        if ($this->hasCsrfProtection()) {
+            $token = $this->csrfProvider->generateCsrfToken(get_class($this));
+
+            $this->add('hidden', $this->csrfFieldName, array(
+                'data' => $token,
+                'property_path' => null,
+            ));
+        }
+    }
+
+    public function setDataClass($class)
+    {
+        $this->dataClass = $class;
+
+        return $this;
+    }
+
+    public function getDataClass()
+    {
+        return $this->dataClass;
+    }
+
+    public function getInstance()
+    {
+        $this->buildCsrfProtection();
+
+        $instance = new Form(
+            $this->getName(),
+            $this->buildDispatcher(),
+            $this->buildRenderer(),
+            $this->getDataTransformer(),
+            $this->getNormalizationTransformer(),
+            $this->getDataMapper(),
+            $this->getDataValidator(),
+            $this->getRequired(),
+            $this->getDisabled(),
+            $this->getAttributes()
+        );
+
+        foreach ($this->buildFields() as $field) {
+            $instance->add($field);
+        }
+
+        if ($this->getData()) {
+            $instance->setData($this->getData());
+        }
+
+        return $instance;
+    }
+}

+ 2 - 8
src/Symfony/Component/Form/Type/FieldType.php

@@ -11,10 +11,8 @@
 
 namespace Symfony\Component\Form\Type;
 
-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;
 use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
 use Symfony\Component\Form\Renderer\Plugin\FieldPlugin;
@@ -27,16 +25,12 @@ use Symfony\Component\Validator\ValidatorInterface;
 
 class FieldType extends AbstractFieldType
 {
-    private $csrfProvider;
-
     private $theme;
 
     private $validator;
 
-    public function __construct(CsrfProviderInterface $csrfProvider,
-            ThemeInterface $theme, ValidatorInterface $validator)
+    public function __construct(ThemeInterface $theme, ValidatorInterface $validator)
     {
-        $this->csrfProvider = $csrfProvider;
         $this->theme = $theme;
         $this->validator = $validator;
     }
@@ -93,7 +87,7 @@ class FieldType extends AbstractFieldType
 
     public function createBuilder(array $options)
     {
-        return new FieldBuilder($this->theme, new EventDispatcher(), $this->csrfProvider);
+        return new FieldBuilder($this->theme, new EventDispatcher());
     }
 
     public function getParent(array $options)

+ 18 - 1
src/Symfony/Component/Form/Type/FormType.php

@@ -12,13 +12,25 @@
 namespace Symfony\Component\Form\Type;
 
 use Symfony\Component\Form\FieldBuilder;
-use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\Form\FormBuilder;
+use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
 use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
 use Symfony\Component\Form\DataMapper\ObjectMapper;
 use Symfony\Component\Form\Renderer\Plugin\FormPlugin;
+use Symfony\Component\EventDispatcher\EventDispatcher;
 
 class FormType extends AbstractFieldType
 {
+    private $theme;
+
+    private $csrfProvider;
+
+    public function __construct(ThemeInterface $theme, CsrfProviderInterface $csrfProvider)
+    {
+        $this->theme = $theme;
+        $this->csrfProvider = $csrfProvider;
+    }
+
     public function configure(FieldBuilder $builder, array $options)
     {
         $builder->setAttribute('virtual', $options['virtual'])
@@ -48,6 +60,11 @@ class FormType extends AbstractFieldType
         );
     }
 
+    public function createBuilder(array $options)
+    {
+        return new FormBuilder($this->theme, new EventDispatcher(), $this->csrfProvider);
+    }
+
     public function getParent(array $options)
     {
         return 'field';

+ 2 - 2
src/Symfony/Component/Form/Type/Loader/DefaultTypeLoader.php

@@ -29,8 +29,8 @@ class DefaultTypeLoader implements TypeLoaderInterface
             ValidatorInterface $validator, TemporaryStorage $storage,
             EntityManager $em = null)
     {
-        $this->addType(new Type\FieldType($csrfProvider, $theme, $validator));
-        $this->addType(new Type\FormType());
+        $this->addType(new Type\FieldType($theme, $validator));
+        $this->addType(new Type\FormType($theme, $csrfProvider));
         $this->addType(new Type\CheckboxFieldType());
         $this->addType(new Type\ChoiceFieldType());
         $this->addType(new Type\CollectionFieldType());