Issues

ZF-12027: Zend_Form::GetValues() not rendered congruent with post data

Description

Adding a subform to a Zend_Form, and setting isArray to false will properly add that subform with expected markup, such that the subform creates appropriate array notation (The $_POST data does not show subform values in a nested array).

However, calling the form's getValues() method renders the data differently, in that the returned result will put the subform's values into a sub-array.

Wouldn't it be expected that a request->getPost() and a form->getValues() would have a congruent structure?

Comments

Hi Matthew, can you provide a complete code example to reproduce the problem? Thanks!

Sure:


<?php
class Application_Form_Sub extends Zend_Form
{
    public function init()
    {
        $this->setIsArray(false);

        $this->addElement(
            'Text',
            'subInput',
            array('label' => 'input in a subform',)
        );
    }
}

<?php
class Application_Form_Super extends Zend_Form
{
    public function init()
    {
        $this->addElement(
            'Text',
            'superInput',
            array('label' => 'regular input',)
        );

        $sub = new Application_Form_Sub();
        $this->addSubForm($sub,'sub');

        $this->addElement(
            'Submit',
            'submit',
            array('label' => 'submit',)
        );
    }
}

<?php
class IndexController extends Zend_Controller_Action
{
    public function indexAction
    {
        $form = new Application_Form_Super();
        $this->view->form = $form;
        $request = $this->getRequest();
        if($request->isPost() && $form->isValid($request->getPost())){

            echo '

Post Data:

';
            var_dump ($request->getPost());
            echo '
'; echo '

Form Values

';
            var_dump ($form->getValues());
            echo '
'; } } }

<?php echo $this->form; ?>

Just run the view, put in some values, and see that the two outputted arrays don't have the same structure.

Also, the same issue exists for Zend_Form::getValidValues().

I did a review of the code. I believe I have established a fix:


class Zend_Form ...
{
    ...

    /**
     * Retrieve all form element values
     *
     * All changes made have been marked with comments
     *
     * @param  bool $suppressArrayNotation
     * @return array
     */
    public function getValues($suppressArrayNotation = false)
    {
        //... no changes until the second foreach loop
        
        foreach ($this->getSubForms() as $key => $subForm) {
            $merge = array();
            if (!$subForm->isArray()) {
                //copied and changed this line:
                //$merge[$key] = $subForm->getValues();
                $merge = $subForm->getValues();
            } else {
                $merge = $this->_attachToArray($subForm->getValues(true),
                                               $subForm->getElementsBelongTo());
            }
            $values = $this->_array_replace_recursive($values, $merge);
        }

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

        return $values;
    }

//Regarding getValidValues, it was a little more involved:

    /**
     * Returns only the valid values from the given form input.
     *
     * For models that can be saved in a partially valid state, for example when following the builder,
     * prototype or state patterns it is particularly interessting to retrieve all the current valid
     * values to persist them.
     *
     * All changes have been marked with comments
     *
     * @param  array $data
     * @param  bool $suppressArrayNotation
     * @return array
     */
    public function getValidValues($data, $suppressArrayNotation = false)
    {
        //... no changes until the second foreach loop
        
        foreach ($this->getSubForms() as $key => $form) {
            $merge = array();

            //added a local variable, swapped $data with $subData within this loop
            $subData = isset($data[$key]) ? $data[$key] : $data;

            // copied and changed the following line:
            //if (isset($data[$key]) && !$form->isArray()) {
            if (!$form->isArray()) {
                $tmp = $form->getValidValues($subData);
                if (!empty($tmp)) {
                    //copied and changed the following line:
                    //$merge[$key] = $tmp;
                    $merge = $tmp;
                }
            } else {
                $tmp = $form->getValidValues($data, true);
                if (!empty($tmp)) {
                    $merge = $this->_attachToArray($tmp, $form->getElementsBelongTo());
                }
            }
            $values = $this->_array_replace_recursive($values, $merge);
        }
        if (!$suppressArrayNotation &&
            $this->isArray() &&
            !empty($values) &&
            !$this->_getIsRendered()) {
            $values = $this->_attachToArray($values, $this->getElementsBelongTo());
        }

        return $values;
    }


Hope this helps!

Note, this same problem also affects getMessages(). The fix is nearly identical.


foreach ($this->getSubForms() as $key => $subForm) {
    $merge = $subForm->getMessages(null, true);
    if (!empty($merge)) {
        if (!$subForm->isArray()) {
// OFFENDING LINE: $merge = array($key => $merge);
        } else {
            $merge = $this->_attachToArray($merge,
                                           $subForm->getElementsBelongTo());
        }
        $messages = $this->_array_replace_recursive($messages, $merge);
    }
}

Removing the offending line fixes the problem.