瀏覽代碼

[Form] Errors are mapped to subforms correctly and bubble up again if necessary

Bernhard Schussek 14 年之前
父節點
當前提交
e3ac248617

+ 12 - 12
src/Symfony/Component/Form/Validator/DelegatingValidator.php

@@ -74,6 +74,11 @@ class DelegatingValidator implements FormValidatorInterface
             $namePath .= '.';
         }
 
+        foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
+        {
+            $mapping['/^'.preg_quote($formPath . 'data.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
+        }
+
         $iterator = new VirtualFormIterator($form->getChildren());
         $iterator = new \RecursiveIteratorIterator($iterator);
 
@@ -93,15 +98,10 @@ class DelegatingValidator implements FormValidatorInterface
             if ($child->hasChildren()) {
                 $this->buildFormPathMapping($child, $mapping, $nestedFormPath, $nestedNamePath);
                 $this->buildDataPathMapping($child, $mapping, $nestedDataPath, $nestedNamePath);
-            } else {
-                $mapping['/^'.preg_quote($nestedFormPath, '/').'(?!\w)/'] = $child;
-                $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
             }
-        }
 
-        foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
-        {
-            $mapping['/^'.preg_quote($formPath . 'data.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
+            $mapping['/^'.preg_quote($nestedFormPath, '/').'(?!\w)/'] = $child;
+            $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
         }
     }
 
@@ -111,6 +111,11 @@ class DelegatingValidator implements FormValidatorInterface
             $namePath .= '.';
         }
 
+        foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
+        {
+            $mapping['/^'.preg_quote($dataPath . '.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
+        }
+
         $iterator = new VirtualFormIterator($form->getChildren());
         $iterator = new \RecursiveIteratorIterator($iterator);
 
@@ -126,11 +131,6 @@ class DelegatingValidator implements FormValidatorInterface
                 $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
             }
         }
-
-        foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
-        {
-            $mapping['/^'.preg_quote($dataPath . '.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
-        }
     }
 
     private function buildNamePathMapping(FormInterface $form, array &$forms, $namePath = '')

+ 45 - 0
tests/Symfony/Tests/Component/Form/Validator/DelegatingValidatorTest.php

@@ -15,6 +15,7 @@ use Symfony\Component\Form\FormBuilder;
 use Symfony\Component\Form\FormError;
 use Symfony\Component\Form\PropertyPath;
 use Symfony\Component\Form\Validator\DelegatingValidator;
+use Symfony\Component\Form\DataTransformer\TransformationFailedException;
 use Symfony\Component\Validator\ConstraintViolation;
 use Symfony\Component\Validator\ExecutionContext;
 
@@ -158,6 +159,28 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array($this->getFormError()), $grandChild->getErrors());
     }
 
+    public function testFormErrorsOnChildWithChildren()
+    {
+        $parent = $this->getForm('author');
+        $child = $this->getForm('address');
+        $grandChild = $this->getForm('street');
+
+        $parent->add($child);
+        $child->add($grandChild);
+
+        $this->delegate->expects($this->once())
+            ->method('validate')
+            ->will($this->returnValue(array(
+                $this->getConstraintViolation('children[address].constrainedProp')
+            )));
+
+        $this->validator->validate($parent);
+
+        $this->assertFalse($parent->hasErrors());
+        $this->assertEquals(array($this->getFormError()), $child->getErrors());
+        $this->assertFalse($grandChild->hasErrors());
+    }
+
     public function testFormErrorsOnParentIfNoChildFound()
     {
         $parent = $this->getForm('author');
@@ -230,6 +253,28 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array($this->getFormError()), $child->getErrors());
     }
 
+    public function testDataErrorsOnChildWithChildren()
+    {
+        $parent = $this->getForm('author');
+        $child = $this->getForm('address');
+        $grandChild = $this->getForm('street');
+
+        $parent->add($child);
+        $child->add($grandChild);
+
+        $this->delegate->expects($this->once())
+            ->method('validate')
+            ->will($this->returnValue(array(
+                $this->getConstraintViolation('data.address.constrainedProp')
+            )));
+
+        $this->validator->validate($parent);
+
+        $this->assertFalse($parent->hasErrors());
+        $this->assertEquals(array($this->getFormError()), $child->getErrors());
+        $this->assertFalse($grandChild->hasErrors());
+    }
+
     public function testDataErrorsOnGrandChild()
     {
         $parent = $this->getForm('author');