If you're developing an application that requires a lot of forms, you'll often find yourself repeating the same code. There are good reasons to avoid using a form package: loading a bunch of classes and running through layers of validation will never be as fast as just working with markup and post values.  But if your project doesn't have to be incredibly fast, and you're already taking the standard precautions against resource drain (smart caching, efficient class loading, minimal bootstrapping), a form package can save you coding time, or help you standardize forms across an application without relying on copy/paste.

Once you've decided to use a form package, you'll have to figure out which one is best for the task at hand. As with so many other programming decisions, rolling your own is the way to ruthlessly optimize for the things you care about, but it requires the kind of time investment that might not be feasible. Choosing an existing package may be as simple as noticing one that's part of a library you're already using, or it might take some extensive research on what's out there and what the strengths are for each.

For the purposes of this article, we'll be focusing upon the two big tasks of form building: display and validation. The examples will use Zend_Form, which is large and complex enough to include most of the common ways of doing each. And we'll talk about custom elements, which can be incredibly useful and require attention to both.

Views

One important question to ask yourself when choosing or building a form package is how it's going to fit into the rest of your architecture, and into the rhythms of development for the project.

Most packages offer you the option of auto-building the form markup from the element objects you've added. This has a lot of appeal for back-end developers, for whom writing markup is the least interesting part of the endeavor, and you can get a lot of mileage out of them on plenty of projects.

An administrative back-end is ideal for this kind of use. It's used only by people intimately familiar with the terminology you'll be using for labels, so you're unlikely to have do much UI engineering that would require fine-grain control over the layout. Admin areas tend to have a lot of forms that should all share a look and feel. Using auto-build views means you can describe the look in your base class--any tweaks to the default view can be applied to every form.

You can also make use of auto-build views for a project where you must do demos while you're working on the underlying API: people (particularly people without programming knowledge, but sometimes even programmers do it, too) instinctively associate the design polish of a form with the state of completion of the code underneath it. The raw look of default forms can work in your favor to remind everyone that your API is still under development.

There are, however, good reasons not to let it persist into your production code. If you've inherited a project that uses a form package in this way within an MVC framework, you'll find view blocks that look something like this:

<div id="some-form">
    <?php echo $this->form->render(); ?>
</div>

This is not precisely helpful when you or your front-end developer have been asked to add an image next to the CVV field. Where is all the markup? How do you know where to add it? Instead, you need to find your way into the form object to have a look at some abstracted markup:

$cvv = new Zend_Form_Element_Text('cvv');
$cvv->addValidator(new My_Custom_CVV_Validator());
$cvv->addDescription('A 3 or 4 digit number located on the back of the card.');
$cvv->addLabel('CVV/Security Code');
$cvv->addDecorator('HTMLTag', array('tag' => 'span', 'class' => 'short-entry'));
$this->addElement($cvv);
$this->addDisplayGroup(
   array('card_name','card_type','card_num','exp_month','exp_year','cvv'), 
   'credit_card', 
   array('displayGroupClass' => 'card')
   );

This sort of complexity has a tendency to accumulate over time, too. Before long, you may find yourself with code that breaks a form into columns, wraps tags around elements or just shoehorns blocks of markup in wherever they'll fit. At that point, I'd recommend sleeping in locked rooms, because your front-end developer owns several large kitchen knives and knows where you live.

How, then, do you balance your client's need for you to bring up forms quickly with your front-end developer's need to have a form's markup all in one file?

Personally, I like to keep the overall layout in the view with the important parts plugged in. For example:

<form id="new_widget"
    method="<?php echo $this->form->getMethod(); ?>"
    action="<?php echo $this->form->getAction(); ?>"
    enctype="application/x-www-form-urlencoded"<?php
    if ($this->form->isErrors()) { echo ' class="errors"'; } ?>>
<fieldset class="column">
    <legend>Details</legend>
    <ul class="elements">
        <li id="elem_title">
        <label for="title">Title:</label>
        <p class="note">(will be used to build the url slug)</p>
        <?php echo $this->form->title->renderViewHelper(); ?>
        <?php echo $this->form->title->renderErrors(); ?>
    </li>

. . .and so on. You can see how easy it would be to loop through the elements rather than grabbing them individually, if you were so inclined.

Validation

Let's consider a simple scenario: you must validate a postal code based on country: U.S. against /^\d{5}(-\d{4})?$/, CA against /^[ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d$/, etc. There are four common places to handle validation:

  1. Using validators attached to the elements
  2. Using a central method for the form
  3. Outside of the form class entirely, in the controller code
  4. Using javascript automatically built by the form package
  5. Using javascript elsewhere (e.g., at the bottom of the view)

Element-level validation is the way to go if your country is coming from somewhere outside the form-if, for example, you're using country code subdomains. You can have your form class accept a country code on build, and set the appropriate regex for your element when you add it. (Most packages come with a regex validator, and it's a simple one to write if you're rolling your own.)

But what if you're getting the country from a select box? You'll need a set of regexes and a way to switch between them based upon the value of the select. For this, you'll need custom validation. Some packages have a built-in "callback" option for element validation, while others use custom classes. Zend Form sends the whole form values array to its validation classes as $context, but this is relatively uncommon. Most packages require you to fall back to the central validation method instead.

Centralized validation is typically implemented as a method you can extend (the parent version runs all of the element-level validation) to include rules that apply to the form as a whole. You can use it to switch out which fields are required, too, if the package allows you to update that information on the fly. The main disadvantage to making this your primary method of customized validation is that it's not very re-use friendly. The logic to test a postal code based upon country will have to be copied between forms, or made into a function or class that can be included. (This is another advantage of Zend Form: the validators extend Zend_Validate rather than anything in the Form package, so they can be used on non-form data, without requiring any of the Zend Form classes.)

For obvious reasons, breaking out of the form to do validation outside of it isn't ideal. If it ends up there, it should be an indicator that your package is too hard to use.

Auto-generated javascript is often more trouble than it's worth: you don't usually have much control over how the error messages are displayed, and just like auto-build views, they make front-end developers sad. Worst of all, they don't save you much time. If you're only using the validators that are already part of the package, it's relatively painless, but any custom validation has to be written twice-once for the backend and once for the front end. That gets old pretty fast.

You can get most of the benefit of automatic Javascript by using an AJAX call on submit, but of course it only saves you the difference between the cost of a page load and the cost of an AJAX call. You don't get the benefit of not contacting the server at all, but it's usually sufficient for most forms. Using jquery as an example

var formvals = {}; // grab these with .val() as necessary
$('#myform').submit(function (e) {
    e.preventDefault();
    $.post({
        url: '/path/to/script',
        data: formvals,
        error: function (ret) { showGenericError(ret); },
        success: function (ret) {
            if (ret.success) {
                // go to the next page, show an message, etc
            } else if ('messages' in ret) {
                // show them on the form
            } else {
                showGenericError(ret);
            }
        }
    });
});

You would just need to add something to the backend to pull up your form class, run the validation method on the post data, and return some json to indicate success and or failure, with error messages keyed by element name. Your front-end dev can do whatever he or she wants with them in the callback. Note that using a real submit button also means it will degrade gracefully; if the user has javascript turned off, the form will behave as usual.

Custom javascript is usually best written ad-hoc, per form, with reusable parts included in a central file. Combining the AJAX method above with custom non-ajax validation (like checking password strength as you type) means the front-end and back-end remain separate, which can be a significant benefit when the front-end and back-end developers must work independently.

Custom elements

Most of the time, regular HTML form elements are fine (and if they aren't, there's usually a jquery plugin to fix that), but occasionally you'll have to do something much more complicated and then include it on multiple forms-for example, an image selector that taps into a pool of user-uploaded images. Often the code to support it will be extensive and involve not only markup but also validation, display, and filtering logic. Rather than try to keep a library of functions to do each piece, you can build a custom element that knows about all those parts and slot itself into the regular form flow.

In Zend_Form, this usually means creating a very simple custom form class:

class My_Element extends Zend_Form_Element {
    public $helper = 'myElement';
    public function init () {
        parent::init();
        $this->addValidator(new My_Validate_MyElement());
    }
}

And a view helper (assuming you've already set the view helper path):

class My_Helper_MyElement extends Zend_View_Helper_FormElement {
   public function myElement($name, $value='', $attribs=array(), $options=null) {
     $info = $this->_getInfo($name, $value, $attribs, $options);
     $content = '<div id="my_elem_' . $info['name'] . '">';
     // show the value in an interesting way...
     // load a jquery plugin, whatever
     $content .= '</div>';
     return $content;
   }
}

And, if necessary, a validator:

class My_Validate_MyElement extends Zend_Validate_Abstract {
    const MYBAD = 'myBad';
    protected $_messageTemplates = array(
        self::MYBAD => "%value% is wrong because...',
    );
    public function isValid($value, $context = array()) {
        if (/* some test */) {
            $this->_error(self::MYBAD);
            return false;
        }
        return true;
    }
}

Then you can add this element wherever you need it. . .like so:

$this->addElement(new My_Element());

Much less painful all around.

In conclusion

As you go about bending forms to your will, bear in mind that the choices you make will affect your whole team, and anyone who works on the code after you. Separating layout from your class structure will ensure that your front-end developer doesn't have to learn about the Decorator Pattern, and modular validation will make for a easy transition when you have to offer the form as a web service. These packages aren't as annoying as they can sometimes seem, and they can provide speed now and flexibility later, provided you use them with care.