ZF-9862: Zend_Form get/setElementsBelongTo() doing bad

Description

I saw this patch [ZF-9593] and something wrong with it

If both form and it's subform set isArray(true)


$form = new Zend_Form(array(
    'name' => 'main',
    'isArray' => true,

    'subforms' => array(
        'subform1' => new Zend_Form_SubForm(array(
            'name' => 'sub1',
            'isArray' => true,

            'elements' => array(
                array(
                    'text',
                    'test',
                    array('required' => true)
                )
            )
        )
    ))
));

$subform2 = new Zend_Form_SubForm(array(
    'isArray' => true,
    'name' => 'sub2')
);

$subform2->addElement(
    'text',
    'test',
    array('required' => true)
);

$form
    ->addSubForm($subform2, 'subform2')
    ->addDecorator('FormErrors')
    ->isValid(array())
;

print '
';
print_r($messages = $form->getMessages());
// output:
//    [main] => Array
//            [subform1] => Array
//                    [test] => Array
//                            [isEmpty] => Value is required and can't be empty
//            [subform2] => Array
//                    [test] => Array
//                            [isEmpty] => Value is required and can't be empty
// all fine!

print_r($values = $form->getValues());
//    [main] => Array
//            [subform1] => Array
//                    [test] => ""
//            [subform2] => Array
//                    [sub2] => Array       // extra level !!!
//                            [test] => ""

// formElements decorator calls to setElementsBelongTo() many times
$form->renderFormElements();

print_r($messages2 = $form->getMessages());
//    [main] => Array
//            [main] => Array       // not a typo - its repeated !!!
//                    [subform1] => Array
//                            [test] => Array
//                                    [isEmpty] => Value is required and can't be empty
//                    [subform2] => Array
//                            [test] => Array
//                                    [isEmpty] => Value is required and can't be empty

print_r($values2 = $form->getValues());
//    [main] => Array
//            [main] => Array       // !!!
//                    [subform1] => Array
//                            [test] => 
//                    [subform2] => Array
//                            [test] => 

// will render nothing after FormElements decorator cause getMessages() returns garbage
$form->renderFormErrors();
print '
';

tested with subforms set isArray(false) - results are the same

Comments

First problem, the extra array levels fixed in r22217 and merged into 1.10 release branch.

The second, the repeated array keys, is a result of the lines


...
        if (!$suppressArrayNotation && $this->isArray()) {
            $messages = $this->_attachToArray($messages, $this->getElementsBelongTo());
        }
...

which after calling renderFormElements cause the returned array to have repeated main keys.

A workaround could be to call $form->getMessages(null, true) to suppress Array Notation, cannot think of an easy solution at the moment.

The questions that arise are: - why do you need to set isArray on the main form? - why do you have to call renderFormElements before getValues and getMessages?

  1. I have 3 main forms on the page which are actually same form with different set of subforms and these are zend_dojo_forms - subforms are rendered in a contentpane decorator (and I can't remove it - I need it for my client-site js) and if subforms have the same name contentpanes have the same id and dojo throws 'duplicate identifier'. To not to invent a new name for the same subform in each main form, I've set main form isArray to prepend all fields with it's name.

  2. FormErrors decorator calls to getMeassages(). I've just added it to form, and it was pushed last in decorators chain far after FormElements. And when nothing was rendered I've started to dig to this bug.

P.S. anyway even if I've removed a contentpane decorator - elements in subforms would have duplicating ids - so no workaround, only setting different names by any means

Fixed in trunk r22238 and merged into release branch