Ver Fonte

Merge remote branch 'yethee/delegating_validator'

* yethee/delegating_validator:
  [Form] Fixed path mapping for DelegatingValidator
Fabien Potencier há 14 anos atrás
pai
commit
7de4d28a05

+ 23 - 14
src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php

@@ -132,19 +132,15 @@ class DelegatingValidator implements FormValidatorInterface
         return (array) $groups;
     }
 
-    private function buildFormPathMapping(FormInterface $form, array &$mapping, $formPath = '', $namePath = '')
+    private function buildFormPathMapping(FormInterface $form, array &$mapping, $formPath = 'children', $namePath = '')
     {
-        if ($formPath) {
-            $formPath .= '.';
-        }
-
         if ($namePath) {
             $namePath .= '.';
         }
 
         foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
         {
-            $mapping['/^'.preg_quote($formPath . 'data.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
+            $mapping['/^'.preg_quote($formPath . '.data.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
         }
 
         $iterator = new VirtualFormAwareIterator($form->getChildren());
@@ -155,21 +151,23 @@ class DelegatingValidator implements FormValidatorInterface
             $parts = explode('.', $path, 2);
 
             $nestedNamePath = $namePath . $child->getName();
-            $nestedFormPath = $formPath . 'children[' . $parts[0] . ']';
+
+            if ($child->hasChildren() || isset($parts[1])) {
+                $nestedFormPath = $formPath . '[' . trim($parts[0], '[]') . ']';
+            }
+            else {
+                $nestedFormPath = $formPath . '.data.' . $parts[0];
+            }
 
             if (isset($parts[1])) {
                 $nestedFormPath .= '.data.' . $parts[1];
             }
 
-            $nestedDataPath = $formPath . 'data.' . $path;
-
             if ($child->hasChildren()) {
                 $this->buildFormPathMapping($child, $mapping, $nestedFormPath, $nestedNamePath);
-                $this->buildDataPathMapping($child, $mapping, $nestedDataPath, $nestedNamePath);
             }
 
             $mapping['/^'.preg_quote($nestedFormPath, '/').'(?!\w)/'] = $child;
-            $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
         }
     }
 
@@ -191,13 +189,24 @@ class DelegatingValidator implements FormValidatorInterface
             $path = (string)$child->getAttribute('property_path');
 
             $nestedNamePath = $namePath . $child->getName();
-            $nestedDataPath = $dataPath . '.' . $path;
+
+            if (strpos($path, '[') === 0) {
+                $nestedDataPath = $dataPath . $path;
+            } else {
+                $nestedDataPath = $dataPath . '.' . $path;
+            }
 
             if ($child->hasChildren()) {
+                // Needs when collection implements the Iterator
+                // or for array used the Valid validator.
+                if (is_array($child->getData()) || $child->getData() instanceof \Traversable) {
+                    $this->buildDataPathMapping($child, $mapping, $dataPath, $nestedNamePath);
+                }
+
                 $this->buildDataPathMapping($child, $mapping, $nestedDataPath, $nestedNamePath);
-            } else {
-                $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
             }
+
+            $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
         }
     }
 

+ 69 - 2
tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php

@@ -129,7 +129,7 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->delegate->expects($this->once())
             ->method('validate')
             ->will($this->returnValue(array(
-                $this->getConstraintViolation('children[firstName].constrainedProp')
+                $this->getConstraintViolation('children.data.firstName')
             )));
 
         $this->validator->validate($parent);
@@ -169,7 +169,7 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->delegate->expects($this->once())
             ->method('validate')
             ->will($this->returnValue(array(
-                $this->getConstraintViolation('children[address].children[street].constrainedProp')
+                $this->getConstraintViolation('children[address].data.street')
             )));
 
         $this->validator->validate($parent);
@@ -220,6 +220,36 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->assertFalse($child->hasErrors());
     }
 
+    public function testFormErrorsOnCollectionForm()
+    {
+        $parent = $this->getForm();
+
+        for ($i = 0; $i < 2; $i++) {
+            $child = $this->getForm((string)$i, '['.$i.']');
+            $child->add($this->getForm('firstName'));
+            $parent->add($child);
+        }
+
+        $this->delegate->expects($this->once())
+            ->method('validate')
+            ->will($this->returnValue(array(
+                $this->getConstraintViolation('children[0].data.firstName'),
+                $this->getConstraintViolation('children[1].data.firstName'),
+            )));
+
+        $this->validator->validate($parent);
+
+        $this->assertFalse($parent->hasErrors());
+
+        foreach ($parent as $child) {
+            $grandChild = $child->get('firstName');
+
+            $this->assertFalse($child->hasErrors());
+            $this->assertTrue($grandChild->hasErrors());
+            $this->assertEquals(array($this->getFormError()), $grandChild->getErrors());
+        }
+    }
+
     public function testDataErrorsOnForm()
     {
         $form = $this->getForm();
@@ -358,6 +388,43 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->assertFalse($child->hasErrors());
     }
 
+    public function testDataErrorsOnCollectionForm()
+    {
+        $parent = $this->getForm();
+        $child = $this->getForm('addresses');
+
+        $parent->add($child);
+
+        for ($i = 0; $i < 2; $i++) {
+            $collection = $this->getForm((string)$i, '['.$i.']');
+            $collection->add($this->getForm('street'));
+
+            $child->add($collection);
+        }
+
+        $this->delegate->expects($this->once())
+            ->method('validate')
+            ->will($this->returnValue(array(
+                $this->getConstraintViolation('data[0].street'),
+                $this->getConstraintViolation('data.addresses[1].street')
+            )));
+
+        $child->setData(array());
+        
+        $this->validator->validate($parent);
+
+        $this->assertFalse($parent->hasErrors(), '->hasErrors() returns false for parent form');
+        $this->assertFalse($child->hasErrors(), '->hasErrors() returns false for child form');
+
+        foreach ($child as $collection) {
+            $grandChild = $collection->get('street');
+
+            $this->assertFalse($collection->hasErrors());
+            $this->assertTrue($grandChild->hasErrors());
+            $this->assertEquals(array($this->getFormError()), $grandChild->getErrors());
+        }
+    }
+
     public function testMappedError()
     {
         $parent = $this->getBuilder()