Issues

ZF-3358: Support out of the box validation with belongsTo/elementsBelongTo

Description

h4. Introduction: Zend_Form supports belongsTo (on element level) and elementsBelongTo (on form level) as more explicit and free ways of grouping fields using array notation. The other default way is using SubForms. A key difference between the two approaches is that with SubForms, Zend_Form knows what data structure to expect when validating and populating. So you can pass it the whole $_POST array for example, and it will loop and assign values.

With the belongsTo/elementsBelongTo approach however, the data structure inside a Zend_Form could be 1 level (flat). While in reality, it's going to result in a nested arrays of arrays inside $_POST once submitted. So the developer can no longer pass $_POST and expect Zend_Form to validate against or populate with.

This limitation is noted in the Advanced Zend_Form Usage section in the manual (the last paragraph): bq. Additionally, on the element level, you can specify individual elements may belong to particular arrays using Zend_Form_Element::setBelongsTo() method. However, doing this may cause issues when validating your element, and is not recommended in most cases.

This issue aims at improving the situation and hopefully removing this warning from the manual :)

h4. To reproduce: A full example is attached for convenience, however minimal important parts follow: h5. Zend_Form_Element's belongsTo:


$form->addElement('text', 'recipient', array(
    'label' => 'Ship to',
    'required' => true,
    'belongsTo' => 'shipping',
));

$form->addElement('submit', 'submit', array(
    'label' => 'Submit'.
));

if ($request->isPost()) {
    if ($form->isValid($request->getPost())) {
        // Execution never reach here. Form is never valid.
        echo 'Order Placed!';
    }
}

echo $form->render();

h5. Zend_Form's elementsBelongTo:


$form->setElementsBelongTo('shipping');

$form->addElement('text', 'recipient', array(
    'label' => 'Ship to',
    'required' => true,
));

$form->addElement('submit', 'submit', array(
    'label' => 'Submit'.
));

if ($request->isPost()) {
    if ($form->isValid($request->getPost())) {
        // Execution never reach here. Form is never valid.
        echo 'Order Placed!';
    }
}

echo $form->render();

Comments

A ready to use example demonstrating the problem when using belongsTo (on the element's level). You just need to correct the path to zf.

A ready to use example demonstrating the problem when using setElementsBelongTo (on the form's level). You just need to correct the path to zf.

Attaching a patch which fixes this issue. It does that by parsing the belongsTo/setElementsBelongsTo and dissolving the value from given data (e.g. POST) based on that.

This was an excellently reported issue -- my thanks to you!

If you could provide unit tests for this functionality, that will make my job even easier. Regardless, I'm scheduling for the next mini release.

Thanks Matthew, it's my pleasure! :)

I started writing the tests few days ago, but more importantly, when I ran the test suite of Zend_Form with the patch applied, I've noticed that 2 tests failed. So I decided to examine the situation again, and I'm working on an enhanced patch. But I need help :)

Why does FormElements decorator override the subForm's elementsBelongTo? For example:


$form->setElementsBelongTo('foo');
$subForm->setElementsBelongT('bar');
$subForm->addElement('text', 'name')->name->setLabel('Name');
$form->addSubForm($subForm, 'sub');

Produces:


While I'd expect it to produce:


or


Attaching an updated patch for Zend_Form's elementsBelongTo.

This patch (Zend_Form-Improved_ElementsBelongTo.patch) includes unit tests, and integrates with isValid, isValidPartial, getMessages, getErrors and getValues. It also modifies FormElements decorator to produce the markup honoring elementsBelongTo, a unit test was added for that as well.

Patch reviewed and applied to trunk and 1.5/1.6 release branches.

Today i wondered who the smart guy was who wrote this _dissolveArrayValue() function. And it's really interesting to read your patch because just now these clean methods you wrote are corrupted again somehow. Living Code :) Maybe it's because the _dissolve function is so dreaded that it's hard to follow.