浏览代码

fixed validation of Doctrine proxy objects

Fabien Potencier 14 年之前
父节点
当前提交
082473659e

+ 38 - 0
src/Symfony/Bridge/Doctrine/Validator/EntityInitializer.php

@@ -0,0 +1,38 @@
+<?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\Bridge\Doctrine\Validator;
+
+use Symfony\Bridge\Doctrine\RegistryInterface;
+use Symfony\Component\Validator\ObjectInitializerInterface;
+use Doctrine\ORM\Proxy\Proxy;
+
+/**
+ * Automatically loads proxy object before validation.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class EntityInitializer implements ObjectInitializerInterface
+{
+    protected $registry;
+
+    public function __construct(RegistryInterface $registry)
+    {
+        $this->registry = $registry;
+    }
+
+    public function initialize($object)
+    {
+        if ($object instanceof Proxy) {
+            $this->registry->getEntityManagerForObject($object)->getUnitOfWork()->initializeObject($object);
+        }
+    }
+}

+ 6 - 0
src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml

@@ -34,6 +34,7 @@
 
         <!-- validator -->
         <parameter key="doctrine.orm.validator.unique.class">Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator</parameter>
+        <parameter key="doctrine.orm.validator_initializer.class">Symfony\Bridge\Doctrine\Validator\EntityInitializer</parameter>
     </parameters>
 
     <services>
@@ -66,5 +67,10 @@
             <tag name="validator.constraint_validator" alias="doctrine.orm.validator.unique" />
             <argument type="service" id="doctrine" />
         </service>
+
+        <service id="doctrine.orm.validator_initializer" class="%doctrine.orm.validator_initializer.class%">
+            <tag name="validator.initializer" />
+            <argument type="service" id="doctrine" />
+        </service>
     </services>
 </container>

+ 33 - 0
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php

@@ -0,0 +1,33 @@
+<?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\Bundle\FrameworkBundle\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+class AddValidatorInitializersPass implements CompilerPassInterface
+{
+    public function process(ContainerBuilder $container)
+    {
+        if (!$container->hasDefinition('validator')) {
+            return;
+        }
+
+        $initializers = array();
+        foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) {
+            $initializers[] = new Reference($id);
+        }
+
+        $container->getDefinition('validator')->replaceArgument(2, $initializers);
+    }
+}

+ 2 - 0
src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

@@ -12,6 +12,7 @@
 namespace Symfony\Bundle\FrameworkBundle;
 
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
+use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
 use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
@@ -52,6 +53,7 @@ class FrameworkBundle extends Bundle
         $container->addCompilerPass(new RegisterKernelListenersPass());
         $container->addCompilerPass(new TemplatingPass());
         $container->addCompilerPass(new AddConstraintValidatorsPass());
+        $container->addCompilerPass(new AddValidatorInitializersPass());
         $container->addCompilerPass(new FormPass());
         $container->addCompilerPass(new TranslatorPass());
         $container->addCompilerPass(new AddCacheWarmerPass());

+ 1 - 0
src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml

@@ -23,6 +23,7 @@
         <service id="validator" class="%validator.class%">
             <argument type="service" id="validator.mapping.class_metadata_factory" />
             <argument type="service" id="validator.validator_factory" />
+            <argument type="collection" />
         </service>
 
         <service id="validator.mapping.class_metadata_factory" class="%validator.mapping.class_metadata_factory.class%" public="false">

+ 10 - 1
src/Symfony/Component/Validator/GraphWalker.php

@@ -32,13 +32,15 @@ class GraphWalker
     protected $context;
     protected $validatorFactory;
     protected $metadataFactory;
+    protected $validatorInitializers = array();
     protected $validatedObjects = array();
 
-    public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory)
+    public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array())
     {
         $this->context = new ExecutionContext($root, $this, $metadataFactory);
         $this->validatorFactory = $factory;
         $this->metadataFactory = $metadataFactory;
+        $this->validatorInitializers = $validatorInitializers;
     }
 
     /**
@@ -60,6 +62,13 @@ class GraphWalker
      */
     public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
     {
+        foreach ($this->validatorInitializers as $initializer) {
+            if (!$initializer instanceof ObjectInitializerInterface) {
+                throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
+            }
+            $initializer->initialize($object);
+        }
+
         $this->context->setCurrentClass($metadata->getClassName());
 
         if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {

+ 30 - 0
src/Symfony/Component/Validator/ObjectInitializerInterface.php

@@ -0,0 +1,30 @@
+<?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\Validator;
+
+/**
+ * Interface for object initializers.
+ *
+ * Concrete implementations of this interface are used by the GraphWalker
+ * to initialize objects just before validating them/
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface ObjectInitializerInterface
+{
+    /**
+     * Initializes an object just before validation.
+     *
+     * @param object $object The object to validate
+     */
+    function initialize($object);
+}

+ 5 - 2
src/Symfony/Component/Validator/Validator.php

@@ -28,14 +28,17 @@ class Validator implements ValidatorInterface
 {
     protected $metadataFactory;
     protected $validatorFactory;
+    protected $validatorInitializers;
 
     public function __construct(
         ClassMetadataFactoryInterface $metadataFactory,
-        ConstraintValidatorFactoryInterface $validatorFactory
+        ConstraintValidatorFactoryInterface $validatorFactory,
+        array $validatorInitializers = array()
     )
     {
         $this->metadataFactory = $metadataFactory;
         $this->validatorFactory = $validatorFactory;
+        $this->validatorInitializers = $validatorInitializers;
     }
 
     /**
@@ -102,7 +105,7 @@ class Validator implements ValidatorInterface
 
     protected function validateGraph($root, \Closure $walk, $groups = null)
     {
-        $walker = new GraphWalker($root, $this->metadataFactory, $this->validatorFactory);
+        $walker = new GraphWalker($root, $this->metadataFactory, $this->validatorFactory, $this->validatorInitializers);
         $groups = $groups ? (array) $groups : array(Constraint::DEFAULT_GROUP);
 
         foreach ($groups as $group) {