Kaynağa Gözat

[Form] Improved error mapping in DelegatingValidator

Bernhard Schussek 14 yıl önce
ebeveyn
işleme
770d403a52

+ 60 - 11
src/Symfony/Component/Form/Validator/DelegatingValidator.php

@@ -35,7 +35,9 @@ class DelegatingValidator implements FormValidatorInterface
             $mapping = array();
             $forms = array();
 
-            $this->buildMapping($form, $mapping, $forms);
+            $this->buildFormPathMapping($form, $mapping);
+            $this->buildDataPathMapping($form, $mapping);
+            $this->buildNamePathMapping($form, $forms);
             $this->resolveMappingPlaceholders($mapping, $forms);
 
             // Validate the form in group "Default"
@@ -61,17 +63,16 @@ class DelegatingValidator implements FormValidatorInterface
         }
     }
 
-    private function buildMapping(FormInterface $form, array &$mapping,
-            array &$forms, $namePath = '', $formPath = '', $dataPath = 'data')
+    private function buildFormPathMapping(FormInterface $form, array &$mapping, $formPath = '', $namePath = '')
     {
-        if ($namePath) {
-            $namePath .= '.';
-        }
-
         if ($formPath) {
             $formPath .= '.';
         }
 
+        if ($namePath) {
+            $namePath .= '.';
+        }
+
         $iterator = new VirtualFormIterator($form->getChildren());
         $iterator = new \RecursiveIteratorIterator($iterator);
 
@@ -86,25 +87,73 @@ class DelegatingValidator implements FormValidatorInterface
                 $nestedFormPath .= '.data.' . $parts[1];
             }
 
-            $nestedDataPath = $dataPath . '.' . $path;
+            $nestedDataPath = $formPath . 'data.' . $path;
 
             if ($child->hasChildren()) {
-                $this->buildMapping($child, $mapping, $forms, $nestedNamePath, $nestedFormPath, $nestedDataPath);
+                $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;
             }
-
-            $forms[$nestedNamePath] = $child;
         }
 
         foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
         {
             $mapping['/^'.preg_quote($formPath . 'data.' . $nestedDataPath).'(?!\w)/'] = $namePath . $nestedNamePath;
+        }
+    }
+
+    private function buildDataPathMapping(FormInterface $form, array &$mapping, $dataPath = 'data', $namePath = '')
+    {
+        if ($namePath) {
+            $namePath .= '.';
+        }
+
+        $iterator = new VirtualFormIterator($form->getChildren());
+        $iterator = new \RecursiveIteratorIterator($iterator);
+
+        foreach ($iterator as $child) {
+            $path = (string)$child->getAttribute('property_path');
+
+            $nestedNamePath = $namePath . $child->getName();
+            $nestedDataPath = $dataPath . '.' . $path;
+
+            if ($child->hasChildren()) {
+                $this->buildDataPathMapping($child, $mapping, $nestedDataPath, $nestedNamePath);
+            } else {
+                $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 = '')
+    {
+        if ($namePath) {
+            $namePath .= '.';
+        }
+
+        $iterator = new VirtualFormIterator($form->getChildren());
+        $iterator = new \RecursiveIteratorIterator($iterator);
+
+        foreach ($iterator as $child) {
+            $path = (string)$child->getAttribute('property_path');
+
+            $nestedNamePath = $namePath . $child->getName();
+            $forms[$nestedNamePath] = $child;
+
+            if ($child->hasChildren()) {
+                $this->buildNamePathMapping($child, $forms, $nestedNamePath);
+            }
+
+        }
+    }
+
     private function resolveMappingPlaceholders(array &$mapping, array $forms)
     {
         foreach ($mapping as $pattern => $form) {

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

@@ -235,6 +235,28 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(array($this->getFormError()), $grandChild->getErrors());
     }
 
+    public function testDataErrorsOnGrandChild2()
+    {
+        $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].data.street.constrainedProp')
+            )));
+
+        $this->validator->validate($parent);
+
+        $this->assertFalse($parent->hasErrors());
+        $this->assertFalse($child->hasErrors());
+        $this->assertEquals(array($this->getFormError()), $grandChild->getErrors());
+    }
+
     public function testDataErrorsOnParentIfNoChildFound()
     {
         $parent = $this->getForm('author');