ZF-9348: Zend_Form isValid() does not validate SubForm with equal named Child Element

Description

When adding a SubForm with Name foo and an Element to that SubForm which is named foo as well, isValid fails to validate the Elements Value.

Following is happening: the call


$data = $this->_dissolveArrayValue($data, $this->getElementsBelongTo());

will convert


$data = array('foo'=> array('bar' => $wantedData));
$elementsBelongTo = foo[bar]

into


$data = $wantedData;

But only if isArray() === true.

So when a SubForm which (isArray() === true) by default, has a child with the same name


$subforname  = 'baz'
$elementname = 'baz'
$elementsBelongTo = 'foo[bar]'
$data = array('foo' => array('bar' => array('baz' => 'Element Value')));

($data = array('baz' => 'Element Value')) === $this->_dissolveArrayValue($data, $elementsBelongTo);

and then later on


if (isset($data[$key])) {
    /**
     * yes because array('baz' => 'Element Value');
     */
    $valid = $form->isValid($data[$key]) && $valid;
    /**
     * OOuuuchh
     */
}

My Statement is that _dissolveArrayValue should be called even when !isArray() because that does not matter anyway as ElementsBelongTo returns $formName when !isArray() and then _dissolveArrayValue will dissolve for $formName instead of $elementsBelongTo.

So now i know that this is wrong because $form->getElementsBelongTo() returns the name only when (true === $form->isArray() && null === $form->_elementsBelongTo)

So the right solution is


if (isset($data[$key]) && !$form->isArray()) {
    /**
     * now we can be sure that $this->_dissolveArrayValue did not dissolve anything
     */
    $valid = $form->isValid($data[$key]) && $valid;
}

Word.

This patch definitely fixes the issue.


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 testIsValidEqualSubFormAndElementName()
+    {
+        $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->isValid($foo));
+
+        $this->form->foo->setIsArray(false);
+
+        $this->assertTrue($this->form->isValid($foo));
+    } 
+
+
     // Display groups
 
     public function testCanAddAndRetrieveSingleDisplayGroups()
Index: library/Zend/Form.php
===================================================================
--- library/Zend/Form.php       (Revision 21667)
+++ library/Zend/Form.php       (Arbeitskopie)
@@ -2044,7 +2044,7 @@
             if (null !== $translator && !$form->hasTranslator()) {
                 $form->setTranslator($translator);
             }
-            if (isset($data[$key])) {
+            if (isset($data[$key]) && !$form->isArray()) {
                 $valid = $form->isValid($data[$key]) && $valid;
             } else {
                 $valid = $form->isValid($data) && $valid;

And for Rob :)


    public function testIsValidEqualSubFormAndElementName()
    {
        $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->isValid($foo));

        $this->form->foo->setIsArray(false);

        $this->assertTrue($this->form->isValid($foo));
    }

Comments

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

Patch applied to trunk and 1.10 release branch.