ZF-9551: Zend_Form isValidPartial Errors when validating a SubForm with an equal named Element

Description

When adding a SubForm to Form and adding An Element to that SubForm which has the same name as the SubForm, isValidPartial will error with 1) Zend_Form_FormTest::testIsValidPartialEqualSubFormAndElementName Invalid argument supplied for foreach()

/var/www/FRAMEWORKS/PHP/ZF/trunk/library/Zend/Form.php:2080 /var/www/FRAMEWORKS/PHP/ZF/trunk/library/Zend/Form.php:2090 /var/www/FRAMEWORKS/PHP/ZF/trunk/tests/Zend/Form/FormTest.php:1487

Based on what i have learned from patching the related issue in isValid i updatet the patch below, for the description why the Error occures see [ZF-9348]

To make the method isValidPartial() more readable and to be able to apply a solution the same way as above i changed the iteration from


foreach($data as $key => $val) {
    if (null !== $this->getElement($key)) {
        // validate Element
    }
    if (null !== $this->getSubForm($key)) {
        $form->isValidPartial($data[$key]);
    }
}
foreach($nonValidatedSubForms as $form) {
    $form->isValidPartial($data);
}

into


foreach ($this->getElements() as $key => $element) {
     if (isset($data[$key])) {
         // validate Element
     }
}
foreach ($this->getSubForms() as $key => $form) {
     if (isset($data[$key]) && !$form->isArray()) { // Why !$form->isArray() see @[ZF-9348]
         $form->isValidPartial($data[$key]);
     } else {
         $form->isValidPartial($data);
     }
}

Which in my Opinion is basically the same.

In Full Notation Before Patch



        foreach ($data as $key => $value) {
            if (null !== ($element = $this->getElement($key))) {
                if (null !== $translator && !$element->hasTranslator()) {
                    $element->setTranslator($translator);
                }
                $valid = $element->isValid($value, $data) && $valid;
            } elseif (null !== ($subForm = $this->getSubForm($key))) {
                if (null !== $translator && !$subForm->hasTranslator()) {
                    $subForm->setTranslator($translator);
                }
                $valid = $subForm->isValidPartial($data[$key]) && $valid;
                $validatedSubForms[] = $key;
            }
        }
        foreach ($this->getSubForms() as $key => $subForm) {
            if (!in_array($key, $validatedSubForms)) {
                if (null !== $translator && !$subForm->hasTranslator()) {
                    $subForm->setTranslator($translator);
                }

                $valid = $subForm->isValidPartial($data) && $valid;
            }
        }

And After Patch


        foreach ($this->getElements() as $key => $element) {
            if (isset($data[$key])) {
                if (null !== $translator && !$element->hasTranslator()) {
                    $element->setTranslator($translator);
                }
                $valid = $element->isValid($data[$key], $data) && $valid;
            }
        }
        foreach ($this->getSubForms() as $key => $form) {
            if (null !== $translator && !$form->hasTranslator()) {
                $form->setTranslator($translator);
            }
            if (isset($data[$key]) && !$form->isArray()) {
                $valid = $form->isValidPartial($data[$key]) && $valid;
            } else {
                $valid = $form->isValidPartial($data) && $valid;
            }
        }

This tested patch fixes the issue, Unit Test included


Index: tests/Zend/Form/FormTest.php
===================================================================
--- tests/Zend/Form/FormTest.php        (Revision 21667)
+++ tests/Zend/Form/FormTest.php        (Arbeitskopie)
@@ -1473,7 +1473,25 @@
         $this->assertTrue($this->form->isValid($data));
     }
 
+    public function testIsValidPartialEqualSubFormAndElementName()
+    {
+        $this->form->addSubForm(new Zend_Form_SubForm(), 'foo')
+                   ->foo->addElement('text', 'foo')
+                        ->foo->setRequired(true)
+                             ->addValidator('Identical',
+                                            false,
+                                            array('Foo Value'));
+        $foo = array('foo' =>
+                     array('foo' => 'Foo Value'));
 
+        $this->assertTrue($this->form->isValidPartial($foo));
+
+        $this->form->foo->setIsArray(false);
+
+        $this->assertTrue($this->form->isValidPartial($foo));
+    } 
+
+
     // Display groups
 
     public function testCanAddAndRetrieveSingleDisplayGroups()
Index: library/Zend/Form.php
===================================================================
--- library/Zend/Form.php       (Revision 21667)
+++ library/Zend/Form.php       (Arbeitskopie)
@@ -2077,30 +2077,24 @@
 
         $translator        = $this->getTranslator();
         $valid             = true;
-        $validatedSubForms = array();
 
-        foreach ($data as $key => $value) {
-            if (null !== ($element = $this->getElement($key))) {
+        foreach ($this->getElements() as $key => $element) {
+            if (isset($data[$key])) {
                 if (null !== $translator && !$element->hasTranslator()) {
                     $element->setTranslator($translator);
                 }
-                $valid = $element->isValid($value, $data) && $valid;
-            } elseif (null !== ($subForm = $this->getSubForm($key))) {
-                if (null !== $translator && !$subForm->hasTranslator()) {
-                    $subForm->setTranslator($translator);
-                }
-                $valid = $subForm->isValidPartial($data[$key]) && $valid;
-                $validatedSubForms[] = $key;
+                $valid = $element->isValid($data[$key], $data) && $valid;
             }
         }
-        foreach ($this->getSubForms() as $key => $subForm) {
-            if (!in_array($key, $validatedSubForms)) {
-                if (null !== $translator && !$subForm->hasTranslator()) {
-                    $subForm->setTranslator($translator);
-                }
-
-                $valid = $subForm->isValidPartial($data) && $valid;
+        foreach ($this->getSubForms() as $key => $form) {
+            if (null !== $translator && !$form->hasTranslator()) {
+                $form->setTranslator($translator);
             }
+            if (isset($data[$key]) && !$form->isArray()) {
+                $valid = $form->isValidPartial($data[$key]) && $valid;
+            } else {
+                $valid = $form->isValidPartial($data) && $valid;
+            }
         }
 
         $this->_errorsExist = !$valid;

Comments

Updatet the Test, it checks now against $form->setIsArray(false) as well.

Patch applied in trunk and 1.10 release branch.