WJBlog


How I Learned to Stop Worrying and Love Zend_Form

Published On: August 19, 2011 @ 10:19:34am


I’ve long been a fan of the Zend Framework, insomuch that I’ve penned a book on the topic, introduced dozens of students to the framework via several full day workshops, and have contributed well over a framework-related articles to Developer.com and PHPBuilder.com, among others (a compilation of which you can view here). It is a fantastically productive framework, one which I happen to use almost every single day.

There was however one feature which absolutely drove me crazy. The Zend_Form component’s uses the dd, dl, and dt elements as the default form markup decorators, meaning that even a simple contact form consisting of name, email, and message fields and a submit button looks like this:


<form id="contact" enctype="application/x-www-form-urlencoded" 
    method="post" action="/about/contact">
  <dl class="zend_form">
    <dt id="name-label"><label for="name" class="required">Your Name:</label></dt>
    <dd id="name-element">
      <input type="text" name="name" id="name" value="" size="35">
    </dd>
    <dt id="email-label"><label for="email" class="required">Your E-mail Address:</label></dt>
    <dd id="email-element">
      <input type="text" name="email" id="email" value="" size="35">
    </dd>
    <dt id="message-label"><label for="message" class="required">Your Message:</label></dt>
    <dd id="message-element">
      <textarea name="message" id="message" rows="10" cols="55"></textarea>
    </dd>
    <dt id="submit-label"> </dt>
    <dd id="submit-element">
      <input type="submit" name="submit" id="submit" value="Send Your Message!">
    </dd>
  </dl>
</form>

While I am not disputing the wisdom of this decision, because there is technically nothing wrong with the approach. However it goes without saying that the overwhelming majority of developers do not use these elements to mark up their forms, with the sheer number of questions posted to StackOverflow and elsewhere about getting rid of these decorators backing this assertion.

Now for the record you can override these decorators in your form models using the setDecorators() method, and you can even create custom decorators, however given my very questionable design skills I’d rather leave all layout decisions to the designer (whether it be choice of markup or CSS stylings). So what’s the solution?

I had previously been using the removeDecorator() method to rip out all of the default decorators and then using the setDecorators() method to identify a custom view script (the latter of which is demonstrated below), however during a Zend Framework workshop I was running in Milwaukee a few months ago Joel Clermont identified a far more succinct method. This method is described below.

Have Your Cake And Eat It Too

As it turns out, there is a way to continue taking advantage of all of the great features Zend_Form has to offer while wielding total control over your form layout! Returning to the aforementioned contact form, I’ll begin the task of overriding all of the decorator defaults by creating a custom form layout file, calling it something like _form_contact.phtml, and placing it within the project path. This file looks like this:

<form id="contact" action="<?= $this->element->getAction(); ?>" 
      method="<?= $this->element->getMethod(); ?>">

<p>
Your Name<br />
<?= $this->element->name; ?>
</p>

<p>
Your E-mail Address<br />
<?= $this->element->email; ?>
</p>

<p>
Your Message<br />
<?= $this->element->message; ?>
</p>

<p>
<?= $this->element->submit ?>
</p>

</form>

Now for the magic. Open your contact form model, and add just two lines to the init() method, both of which are bolded below:

<?php

class Application_Form_Contact extends Zend_Form
{

  public function init()
  {
    $this->setName('contact');
    $this->setMethod('post');
    $this->setAction('/about/contact');

    $name = new Zend_Form_Element_Text('name');
    $name->setAttrib('size', 35);
    $name->setLabel('Your Name:');
    $name->setRequired(true);
    $name->addErrorMessage('Please provide your name');

    $email = new Zend_Form_Element_Text('email');
    $email->setAttrib('size', 35);
    $email->setLabel('Your E-mail Address:');
    $email->setRequired(true);
    $email->addErrorMessage('Please provide a valid e-mail address');

    $message = new Zend_Form_Element_Textarea('message');
    $message->setLabel('Your Message:');
    $message->setRequired(true);
    $message->setAttrib('rows', '10');
    $message->setAttrib('cols', '55');
    $message->addErrorMessage('Please specify a message');

    $submit = new Zend_Form_Element_Submit('submit');
    $submit->setLabel('Send Your Message!');

    $this->setDecorators( array( 
    array('ViewScript', array('viewScript' => '_form_contact.phtml'))));

    $this->addElements(array($name, $email, $message, $submit));

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

}

The setDecorators() call will tell the Zend Framework to use the _form_contact.phtml script as the form’s render template, and the setElementDecorators() method will strip all of the default layout decorators which are otherwise rendered. There are numerous variations in terms of exactly what defaults you can override, but I’m trying to stay on topic and so won’t get into them here. What is clear is that you must set setDecorators() before calling addElements(), and you must all setElementDecorators() after calling addElements(). With the updated model in place, the rendered form looks like this:

<form id="contact" action="/about/contact" method="post">

<p>
Your Name<br />
<input type="text" name="name" id="name" value="" size="35">
</p>

<p>
Your E-mail Address<br />
<input type="text" name="email" id="email" value="" size="35">
</p>

<p>
Your Message<br />
<textarea name="message" id="message" rows="10" cols="55"></textarea>
</p>

<p>
<input type="submit" name="submit" id="submit" value="Send Your Message!">
</p>

</form>

And that’s how I learned to stop worrying and love Zend_Form.

Disclaimer: I cover a close variant of this approach in the second edition of Easy PHP Websites with the Zend Framework, however the specific approach described above is in my opinion preferable to all others. I’ll update the book to reflect this approach in a forthcoming version.