Zend_Form’s flaw in an MVC environment

Zend_Form, the latest and greatest addition to the Zend Framework in version 1.5, is an infusion of the best bits of Zend_Filter_Input, and the Zend_View_Helper system. At first glance it looks like the ideal system for setting up from the simplest to the most complex forms, and this is how most people will see it.

I recently delved into the use of it, and was instantly shocked at a fatal flaw in its design. Where am I supposed to put it?

I try to code in as best practices as I can, and so even when I’m not using the Zend MVC implementation, I’m still trying to keep to MVC style principles. Essentially I always try to separate control logic from view layout.

Zend_Form is about as bad as it gets at separating this. Initialisation involves specifying Zend_Form_Element’s which directly map to their associated Zend_View_Helper’s.

// controller
$form = new Zend_Form();
$form
    ->setAction('/submit')
    ->setMethod('post')
    ->addElement('text', 'user-name', array(
        'required' => true,
        'default' => 'Fred',
        'validators' => array(
            array('stringLength', 1, 20),
        )
    ))
    ->addElement('textarea', 'user-comment', array(
        'required' => true,
        'validators' => array(
            array('stringLength', 1, 255),
        )
    ))
    ->addElement('submit', 'action', array(
        'label' => 'Submit',
    ));

if ($_POST['action']) {
    if ($form->isValid()) {
        // insert comment
    }
}

// view
echo $form->render();

This snipett of code will create a simple form with a textbox and textarea in. If submitted, the form will be validated, and the comment will be added if it passes.

The assumption is that you put the form initialisation in the controller, as that is where validation and database insertation should be performed. However, that means the controller has to define the HTML tag type of each element. e.g. in this example, that user-name is an <input type=”text” />, and user-comment is a <textarea></textarea>.

This view layout information, not control logic. The only place this should be put is in the view script, however the above is the only way, hence this control is not suited to MVC practices.

My first thought was, how can this be done better MVC style? The trouble is it may be possible that a display element would be complex, and that it needs control logic associated with it. An element may be a scalar, or contain multiple predefined options.

So, this could be represented by declaring elements as primitive types, such as:

  • string
  • integer
  • enum
  • set

There could also be more complex types which define a bit more control behaviour:

  • date

These types have several obvious HTML element mappings, e.g. strings map to <input type=”text” /> or <textarea></textarea>, enums map to <select></select> or multiple <input type=”radio” />. A developer doesn’t always need to care about enforcing a specific type, so the Form control could choose for itself.

An example of this in use would be:

// controller
$form = new My_Form();
$form
    ->setAction('/submit')
    ->setMethod('post')
    ->addElement('string', 'user-name', array(
        'required' => true,
        'validators' => array(
            array('stringLength', 1, 20),
        ))
    ->addElement('string', 'user-comment', array(
        'required' => true,
        'validators' => array(
            array('stringLength', 1, 255),
        ));

if ($form->posted()) {
    if ($form->isValid()) {
        // insert comment
    }
}

// view
$form->setElementHelper('string', 'text'); // already set as default
$form->addElement('submit', 'action', array(
        'label' => 'Submit',
    ));
$form->getElement('user-comment')->setHelper('textarea');
echo $form->render();

This would create exactly the same form, but conform to MVC standards.