浏览代码

[Form] Extracted data validation logic into DataValidatorInterface

Bernhard Schussek 14 年之前
父节点
当前提交
f86ecec403

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

@@ -21,6 +21,7 @@ use Symfony\Component\Form\Renderer\Plugin\FieldPlugin;
 use Symfony\Component\Form\EventListener\TrimListener;
 use Symfony\Component\Form\EventListener\ValidationListener;
 use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
+use Symfony\Component\Form\DataValidator\DelegatingValidator;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 use Symfony\Component\Validator\ValidatorInterface;
 
@@ -63,10 +64,10 @@ class FieldConfig extends AbstractFieldConfig
             ->setAttribute('validation_groups', $options['validation_groups'])
             ->setValueTransformer($options['value_transformer'])
             ->setNormalizationTransformer($options['normalization_transformer'])
-            ->addEventSubscriber(new ValidationListener($this->validator), -128)
             ->setData($options['data'])
             ->setRenderer(new DefaultRenderer($this->theme, $options['template']))
-            ->addRendererPlugin(new FieldPlugin());
+            ->addRendererPlugin(new FieldPlugin())
+            ->setDataValidator(new DelegatingValidator($this->validator));
 
         if ($options['trim']) {
             $builder->addEventSubscriber(new TrimListener());

+ 19 - 0
src/Symfony/Component/Form/DataValidator/DataValidatorInterface.php

@@ -0,0 +1,19 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\DataValidator;
+
+use Symfony\Component\Form\FieldInterface;
+
+interface DataValidatorInterface
+{
+    function validate(FieldInterface $field);
+}

+ 24 - 35
src/Symfony/Component/Form/EventListener/ValidationListener.php

@@ -9,7 +9,7 @@
  * file that was distributed with this source code.
  */
 
-namespace Symfony\Component\Form\EventListener;
+namespace Symfony\Component\Form\DataValidator;
 
 use Symfony\Component\Form\FieldInterface;
 use Symfony\Component\Form\FormInterface;
@@ -21,7 +21,7 @@ use Symfony\Component\Form\Event\DataEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\Validator\ValidatorInterface;
 
-class ValidationListener implements EventSubscriberInterface
+class DelegatingValidator implements DataValidatorInterface
 {
     private $validator;
 
@@ -30,48 +30,37 @@ class ValidationListener implements EventSubscriberInterface
         $this->validator = $validator;
     }
 
-    public static function getSubscribedEvents()
-    {
-        return array(
-            Events::postBind,
-        );
-    }
-
     /**
      * Validates the form and its domain object
      */
-    public function postBind(DataEvent $event)
+    public function validate(FieldInterface $field)
     {
-        $field = $event->getField();
-
-        if ($field->isRoot()) {
-            // Validate the field in group "Default"
-            // Validation of the data in the custom group is done by validateData(),
-            // which is constrained by the Execute constraint
-            if ($violations = $this->validator->validate($field)) {
-                foreach ($violations as $violation) {
-                    $propertyPath = new PropertyPath($violation->getPropertyPath());
-                    $iterator = $propertyPath->getIterator();
-                    $template = $violation->getMessageTemplate();
-                    $parameters = $violation->getMessageParameters();
-
-                    if ($iterator->current() == 'data') {
-                        $iterator->next(); // point at the first data element
-                        $error = new DataError($template, $parameters);
-                    } else {
-                        $error = new FieldError($template, $parameters);
-                    }
-
-                    $this->mapError($field, $error, $iterator);
+        // Validate the field in group "Default"
+        // Validation of the data in the custom group is done by validateData(),
+        // which is constrained by the Execute constraint
+        if ($violations = $this->validator->validate($field)) {
+            foreach ($violations as $violation) {
+                $propertyPath = new PropertyPath($violation->getPropertyPath());
+                $iterator = $propertyPath->getIterator();
+                $template = $violation->getMessageTemplate();
+                $parameters = $violation->getMessageParameters();
+
+                if ($iterator->current() == 'data') {
+                    $iterator->next(); // point at the first data element
+                    $error = new DataError($template, $parameters);
+                } else {
+                    $error = new FieldError($template, $parameters);
                 }
+
+                $this->mapError($error, $field, $iterator);
             }
         }
     }
 
-    private function mapError(FieldInterface $form, Error $error,
+    private function mapError(Error $error, FieldInterface $field,
             PropertyPathIterator $pathIterator = null)
     {
-        if (null !== $pathIterator && $field instanceof FieldInterface) {
+        if (null !== $pathIterator && $field instanceof FormInterface) {
             if ($error instanceof FieldError && $pathIterator->hasNext()) {
                 $pathIterator->next();
 
@@ -82,7 +71,7 @@ class ValidationListener implements EventSubscriberInterface
                 if ($field->has($pathIterator->current())) {
                     $child = $field->get($pathIterator->current());
 
-                    $this->mapError($child, $error, $pathIterator);
+                    $this->mapError($error, $child, $pathIterator);
 
                     return;
                 }
@@ -97,7 +86,7 @@ class ValidationListener implements EventSubscriberInterface
                                 $pathIterator->next();
                             }
 
-                            $this->mapError($child, $error, $pathIterator);
+                            $this->mapError($error, $child, $pathIterator);
 
                             return;
                         }

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

@@ -16,7 +16,7 @@ namespace Symfony\Component\Form;
  *
  * @author Bernhard Schussek <bernhard.schussek@symfony.com>
  */
-class Error
+abstract class Error
 {
     /**
      * The template for the error message

+ 9 - 2
src/Symfony/Component/Form/Field.php

@@ -13,12 +13,12 @@ namespace Symfony\Component\Form;
 
 use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
 use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
+use Symfony\Component\Form\DataValidator\DataValidatorInterface;
 use Symfony\Component\Form\Renderer\RendererInterface;
 use Symfony\Component\Form\Renderer\Plugin\RendererPluginInterface;
 use Symfony\Component\Form\Event\DataEvent;
 use Symfony\Component\Form\Event\FilterDataEvent;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Base class for form fields
@@ -65,6 +65,7 @@ class Field implements FieldInterface
     private $normalizationTransformer;
     private $valueTransformer;
     private $transformationSuccessful = true;
+    private $dataValidator;
     private $renderer;
     private $disabled = false;
     private $dispatcher;
@@ -73,13 +74,15 @@ class Field implements FieldInterface
     public function __construct($name, EventDispatcherInterface $dispatcher,
         RendererInterface $renderer, ValueTransformerInterface $valueTransformer = null,
         ValueTransformerInterface $normalizationTransformer = null,
-        $required = false, $disabled = false, array $attributes = array())
+        DataValidatorInterface $dataValidator = null, $required = false,
+        $disabled = false, array $attributes = array())
     {
         $this->name = (string)$name;
         $this->dispatcher = $dispatcher;
         $this->renderer = $renderer;
         $this->valueTransformer = $valueTransformer;
         $this->normalizationTransformer = $normalizationTransformer;
+        $this->dataValidator = $dataValidator;
         $this->required = $required;
         $this->disabled = $disabled;
         $this->attributes = $attributes;
@@ -282,6 +285,10 @@ class Field implements FieldInterface
 
         $event = new DataEvent($this, $clientData);
         $this->dispatcher->dispatch(Events::postBind, $event);
+
+        if ($this->isRoot() && $this->dataValidator) {
+            $this->dataValidator->validate($this);
+        }
     }
 
     /**

+ 15 - 0
src/Symfony/Component/Form/FieldBuilder.php

@@ -12,6 +12,7 @@
 namespace Symfony\Component\Form;
 
 use Symfony\Component\Form\DataMapper\DataMapperInterface;
+use Symfony\Component\Form\DataValidator\DataValidatorInterface;
 use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
 use Symfony\Component\Form\Renderer\DefaultRenderer;
 use Symfony\Component\Form\Renderer\RendererInterface;
@@ -55,6 +56,8 @@ class FieldBuilder
 
     private $dataMapper;
 
+    private $dataValidator;
+
     private $attributes = array();
 
     public function __construct(ThemeInterface $theme,
@@ -141,6 +144,16 @@ class FieldBuilder
         return $this->dataMapper;
     }
 
+    public function setDataValidator(DataValidatorInterface $dataValidator)
+    {
+        $this->dataValidator = $dataValidator;
+    }
+
+    public function getDataValidator()
+    {
+        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.
@@ -482,6 +495,7 @@ class FieldBuilder
                 $this->getValueTransformer(),
                 $this->getNormalizationTransformer(),
                 $this->getDataMapper(),
+                $this->getDataValidator(),
                 $this->getRequired(),
                 $this->getDisabled(),
                 $this->getAttributes()
@@ -497,6 +511,7 @@ class FieldBuilder
                 $this->buildRenderer(),
                 $this->getValueTransformer(),
                 $this->getNormalizationTransformer(),
+                $this->getDataValidator(),
                 $this->getRequired(),
                 $this->getDisabled(),
                 $this->getAttributes()

+ 5 - 6
src/Symfony/Component/Form/Form.php

@@ -13,7 +13,6 @@ namespace Symfony\Component\Form;
 
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\FileBag;
-use Symfony\Component\Validator\ValidatorInterface;
 use Symfony\Component\Validator\ExecutionContext;
 use Symfony\Component\Form\Event\DataEvent;
 use Symfony\Component\Form\Event\FilterDataEvent;
@@ -26,6 +25,7 @@ use Symfony\Component\Form\Exception\FieldDefinitionException;
 use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
 use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
 use Symfony\Component\Form\DataMapper\DataMapperInterface;
+use Symfony\Component\Form\DataValidator\DataValidatorInterface;
 use Symfony\Component\Form\Renderer\RendererInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -59,15 +59,13 @@ class Form extends Field implements \IteratorAggregate, FormInterface
      */
     private $extraFields = array();
 
-    private $virtual;
-
     private $dataMapper;
 
     public function __construct($name, EventDispatcherInterface $dispatcher,
         RendererInterface $renderer, ValueTransformerInterface $valueTransformer = null,
         ValueTransformerInterface $normalizationTransformer = null,
-        DataMapperInterface $dataMapper, $required = false, $disabled = false,
-        array $attributes = array())
+        DataMapperInterface $dataMapper, DataValidatorInterface $dataValidator = null,
+        $required = false, $disabled = false, array $attributes = array())
     {
         $dispatcher->addListener(array(
             Events::postSetData,
@@ -79,7 +77,8 @@ class Form extends Field implements \IteratorAggregate, FormInterface
         $this->dataMapper = $dataMapper;
 
         parent::__construct($name, $dispatcher, $renderer, $valueTransformer,
-            $normalizationTransformer, $required, $disabled, $attributes);
+            $normalizationTransformer, $dataValidator, $required, $disabled,
+            $attributes);
     }
 
     /**