ybits (why bits? because.)

September 23rd, 2009

A Zend_Form is (or at least, can be) an unfortunate mish-mash of logical and visual rules. Instead of designing how the form will render visually using HTML in a .phtml file, like everything else that’s rendered, there are a bunch of programmatic hoops to jump through to set up the visual properties in code using decorators.

The hoops aren’t so bad if the layout of the form is simple, for example a basic layout with all the labels to the left and all the elements on the right. But to create a complex form layout using tables (yes, we still use tables for tabular data), the table layout must be created in HTML anyway, and then back-fitted into code by breaking the structure apart and assigning different pieces to individual elements of the form.

This process is tedious, and twice the amount of work to get the same end result. Plus, to change something visually, either the template has to be modified until it’s right and then back-fitted again, or the visual properties have to be tweaked directly in code without the benefit of the template. The latter being the “hope for the best” method.

Mashing this visual structure into the form object itself:

  • Encroaches on the role of the view.
  • Breaks separation of concerns.
  • Is harder than it should be.

The hybrid approach to Zend_Form is to use all of its functionality except the decorators for visual layout. Instead, the form is laid out normally in a view script, and each element of the form is rendered individually where it needs to be.

This process provides the best of both worlds. The form object is still used to define the elements and do validations, but the actual layout is done (once) in HTML, which is easier to create, modify and maintain.

Here’s an example, with bits of a form object, controller, and view script:

MyForm.php:

class MyForm extends Zend_Form
{
    public function __construct($options = null)
    {
        parent::__construct($options);
        $this->setName('my_form');

        $textFilters = array(
            'StripTags',
            'StringTrim'
        );

        $options = array(
            'name' => 'name',
            'required' => true,
            'validators' => array('NotEmpty'),
            'filters' => $textFilters,
            'class' => 'form',
            'size' => 65,
            'required' => true
        ); 

        $input = new Zend_Form_Element_Text($options);
        $this->addElement($input);

        $options = array(
            'name' => 'age',
            'required' => false,
            'validators' => array('NotEmpty'),
            'filters' => $textFilters,
            'class' => 'form',
            'size' => 5,
            'required' => true
        ); 

        $input = new Zend_Form_Element_Text($options);
        $this->addElement($input);

        $element = new Zend_Form_Element_Submit('submitbtn');
        $element->setAttrib('id', 'submitbtn')
                      ->setAttrib('class', 'button')
                      ->setLabel('Save');
        $this->addElement($element);

        $this->setElementDecorators(array(
            'ViewHelper',
            'Errors'
         ));
    }
}

MyController.php:

public function showAction()
{
    $form = new MyForm();
    $form->populate($this->_request->getPost());
    $this->view->form = $form;
}

show.phtml:

<form
name="<?php echo $this->form->getName() ?>"
id="<?php echo $this->form->getName() ?>"
method="post" enctype="application/x-www-form-urlencoded"
action=""
>
<table>
<tr>
<td><?php echo $this->form->getElement('name')->getLabel() ?></td>
<td><?php echo $this->form->getElement('name')?></td>
</tr>

<tr>
<td><?php echo $this->form->getElement('age')->getLabel() ?></td>
<td><?php echo $this->form->getElement('age')?></td>
</tr>

<tr>
<td></td>
<td><?php echo $this->form->getElement('submitbtn')?></td>
</tr>

</table>
</form>