ZF-5222: Zend_Form::getValues() mishandles SubForms data when elementsBelongTo is set
Description
When setting elementsBelongTo for a Zend_Form_SubForm to either an empty value '' (isArray() == false) or to an array such as "__children[Employees][Company][__ids][pk_33]" these methods do not handle the arrays properly:
getValues() getErrors() getMessages()
1.) Not all subforms are arrays, such as when elementsBelongTo is set empty. This causes extra levels in the returned array structure due to calling _attachToArray() with empty $arrayPath. Analogous calls to _dissolveArray() correctly check for isArray().
2.) When collecting data recursively from subforms, only data from the last subform is kept because array_merge() overwrites array entries where the first key is the same among the subforms. Note that array_merge() works for the simple case where just the subform name (or simple elementsBelongTo) is used for the value keys. Using array_merge_recursive() instead of array_merge() fixes this bug when elementsBelongTo is set to a deeper array structure.
For example, sibling subforms may typically have elementsBelongTo set to common base arrays with only the last key differing:
__children[Employees][Company][__ids][pk_33]
__children[Employees][Company][__ids][pk_31]
__children[Employees][Company][__ids][pk_58]
Here is a proposed patch:
Index: Zend/Form.php
===================================================================
--- Zend/Form.php (revision 11917)
+++ Zend/Form.php (working copy)
@@ -1282,8 +1282,20 @@
}
}
foreach ($this->getSubForms() as $key => $subForm) {
- $fValues = $this->_attachToArray($subForm->getValues(true), $subForm->getElementsBelongTo());
- $values = array_merge($values, $fValues);
+ $fValues = $subForm->getValues(true);
+
+ // Not all subForms are arrays, such as when elementsBelongTo is set empty
+ if ($subForm->isArray()) {
+ $fValues = $this->_attachToArray($fValues, $subForm->getElementsBelongTo());
+ }
+
+ // Need to use array_merge_recursive() instead of array_merge() because
+ // elementsBelongTo when set for sibling subForms may typically
+ // have common base arrays; for example:
+ // __children[Users][ManagementCompany][__ids][pk_33]
+ // __children[Users][ManagementCompany][__ids][pk_31]
+ // __children[Users][ManagementCompany][__ids][pk_58]
+ $values = array_merge_recursive($values, $fValues);
}
if (!$suppressArrayNotation && $this->isArray()) {
@@ -2208,8 +2220,20 @@
$errors[$key] = $element->getErrors();
}
foreach ($this->getSubForms() as $key => $subForm) {
- $fErrors = $this->_attachToArray($subForm->getErrors(), $subForm->getElementsBelongTo());
- $errors = array_merge($errors, $fErrors);
+ $fErrors = $subForm->getErrors();
+
+ // Not all subForms are arrays, such as when elementsBelongTo is set empty
+ if ($subForm->isArray()) {
+ $fErrors = $this->_attachToArray($fErrors, $subForm->getElementsBelongTo());
+ }
+
+ // Need to use array_merge_recursive() instead of array_merge() because
+ // elementsBelongTo when set for sibling subForms may typically
+ // have common base arrays; for example:
+ // __children[Users][ManagementCompany][__ids][pk_33]
+ // __children[Users][ManagementCompany][__ids][pk_31]
+ // __children[Users][ManagementCompany][__ids][pk_58]
+ $errors = array_merge_recursive($errors, $fErrors);
}
}
return $errors;
@@ -2261,8 +2285,18 @@
$fMessages = $subForm->getMessages(null, true);
if (!empty($fMessages)) {
if (array_key_exists($key, $arrayKeys)) {
- $fMessages = $this->_attachToArray($fMessages, $arrayKeys[$key]);
- $messages = array_merge($messages, $fMessages);
+ // Not all subForms are arrays, such as when elementsBelongTo is set empty
+ if ($subForm->isArray()) {
+ $fMessages = $this->_attachToArray($fMessages, $arrayKeys[$key]);
+ }
+
+ // Need to use array_merge_recursive() instead of array_merge() because
+ // elementsBelongTo when set for sibling subForms may typically
+ // have common base arrays; for example:
+ // __children[Users][ManagementCompany][__ids][pk_33]
+ // __children[Users][ManagementCompany][__ids][pk_31]
+ // __children[Users][ManagementCompany][__ids][pk_58]
+ $messages = array_merge_recursive($messages, $fMessages);
} else {
$messages[$key] = $fMessages;
}
Comments
Posted by Christian Albrecht (alab) on 2010-03-18T08:37:40.000+0000
getValues() is Fixed in [ZF-9584] and in [ZF-9586].
Posted by Christian Albrecht (alab) on 2010-03-18T10:24:24.000+0000
getErrors Fixed in [ZF-9467]
Posted by Christian Albrecht (alab) on 2010-03-25T13:23:59.000+0000
Reopened because suggested fix is not reviewed and committed yet.
Posted by Christian Albrecht (alab) on 2010-04-02T00:56:03.000+0000
getMessages Fixed in ZF-9593
Posted by Christian Albrecht (alab) on 2010-04-16T09:46:30.000+0000
Resolving as duplicate of ZF-9584, ZF-9586, ZF-9467 and ZF-9593