Simplifying Automated Form Generation, Validation, and Output

First Praise, Then Critique

HTML_QuickForm is the standard class within PEAR for building forms programmatically. Here’s an example:


$form =& new HTML_QuickForm();
$type = 'text';
$name = 'city';
$label = 'Your City:';
$form->addElement($type, $name, $label);

(All XHTML element types are supported, including some pseudo-types such as ‘date’ and ‘time’).

When you call $form->display(), the class builds all the HTML for the form and the elements you have added. That’s just the start, though. If you have a POST variable called ‘city’, it will populate the form with that value for you. You can add validation methods to each element, and the class will check the values and print error messages inside the form if the values are not valid. You can apply special processing to the values before retrieving them as an array with $form->export().

It’s quite a neat package, but I have some quibbles with its operation. These are not design flaws; indeed, its design seems quite internally consistent. For example, each element is represented internally as an object itself, with its own methods and properties (usually descended from a base element class). For example, to set the value of an individual element, you have to call its setValue() method — but any submitted values (e.g., in POST) will override that value. There are ways around this, but none of them seem simple or straightforward.

More to the point, while I am not a strict model-view-controller devotee, I find that HTML_QuickForm not only pierces the veil between model and view (or controller and view, depending on your definition), it positively shreds that veil to pieces. Yes, it has renderers that allow you to parse through the internal objectified representatiion of the form elements, but they system seems overly complex.

While I like HTML_QuickForm, and have written code (in DB_Table) that automatically generates forms from tables using it, I don’t fully comprehend the methodologies underlying its operation. It seems to me there should be a simpler and more straightforward way of automating form generation, one that is both more comprehensible and more open to MVC separate while retaining the obvious strengths of validation and automated population of values.

Savant and Solar

Note: Savant3 and Solar are publicly available but are still in development. I present the remainder of this article as advance information on how they will interoperate, and while I am using the described methodologies on a continuing basis, the specifics may change in the future.

With the above points in mind, I developed a plugin for Savant2 that accepts an array of element definitions and generates forms from it (called Savant2_Plugin_form for obvious reasons). In Savant3, to be released, I have refined and extended that original idea to be a bit more full-featured (and I intend to backport it to Savant2, so don’t worry about missing out). The Savant plugins cover the ‘view’ half of form generation. For the ‘model’ (or the ‘controller’ depending on how you think about these things) portion, I have implemented a complementary system in Solar_Form to collect and manage these element arrays.

The key here is that the form generation happens not in one location, as in HTML_QuickForm, but in two: Solar_Form defines the form elements, and Savant renders it. One benefit of this separation of duties is that neither class cares what the other class is. If you like, you can send a hand-built array of elements to Savant, and it will build the form just the same. Similarly, you can pass a Solar_Form element array to any template system, and if your template system knows how to deal with that array, it can render a form too. The point is that the information about the form is transmitted in a standard format as an array.

Therefore, the format of the element array is of paramount importance. Lucky for us, it is quite simple. For each element in the form, you have an array that looks like this:


$element = array(

    // the 'name' attribute for the XHTML form element
    'name'     => null,

    // the 'type' attribute
    'type'     => null,

    // a text label for the element
    'label'    => null,

    // the 'value' attribute of the element
    'value'    => null,

    // whether or not the element is required
    'require'  => false,

    // whether or not the element is disabled
    // (i.e., 'read-only')
    'disable'  => false,

    // any options related to the element values,
    // typically for select options, radios, or
    // checkbox values
    'options'  => array(),

    // additional attributes for the XHTML form element
    'attribs'  => array()
);

From our earlier example above, you would create an element under this system simply by specifying the array keys for it:


$element = array(
    'name' => 'city',
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

The Savant3 template system can take this array and render a form within a template script like this.


// assume that $element has been assigned as 'elem'
$this->form('start');
$this->form('auto', $this->elem);
$this->form('end');

Note the use of the ‘auto’ call, which automatically builds a single form element from an array; if you use the ‘fullauto’ call, it will build multiple elements from a sequential array of elements, in order.

The above discussion only describes the manual generation of form elements (the display is automated via Savant3). To get more of the functionality of HTML_QuickForm, we would need to use Solar_Form.

Solar_Form collects an array of elements together in a class property; you can add them one-by-one like this:


$form = Solar::object('Solar_Form');
$element = array(
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

// note that the 'name' key is optional when
// using setElement(); Solar_Form will use the
// first parameter as the 'name' value, overriding
// the 'name' key.  this has benefits when
// building arrays of elements.
$form->setElement('city', $element);

To populate the form elements with their related POST values, simply call the populate() method.


$form->populate();

You can then pass that form to Savant3 …


$Savant3->form_elements = $form->elements;

… and render the form in a template script like so:


$this->form('start');
$this->form('fullauto', $this->form_elements);
$this->form('end');

Finally, you can add automated validation to the Solar_Form elements with the addValidate() method. Below, the $method relates to a standard Solar_Valid method; in addition to ‘alpha’, there are ‘alphanumeric’, ‘numeric’, ‘isoDate’, ’email’, and other validation methods, as well as ‘regex’ (for user-defined regular expressions), ‘custom’ (for call_user_func callbacks), and ‘multiple’ (for arrays of validation routines).


$form = Solar::object('Solar_Form');

$element = array(
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

$form->setElement('city', $element);

$name = 'city';
$method = 'alpha';
$feedback = 'Please use only letters in the city name';
$form->addValidate($name, $method, $feedback);

Then you can call the validate() method, which will return true or false, and will automatically set the feedback messages for elements that failed validation.


if ($form->validate()) {
    $values = $form->values();
    // insert values into a table
}

$Savant3->form_elements = $form->elements;

Conclusion

While I shamelessly promote Solar and Savant throughout this entry, my main goal is to point out that MVC separation of form building, validation, automated population, and output display is possible with a unified format describing the form elements. It is not necessary to wrap all those functions into one class; indeed, separating the concerns seems vital to extended development cycles when differing framework systems must interoperate. Having it all in one class is great to start with, but after a while you find yourself coding to the class and trying to learn its intricacies, instead of using it for your own purposes.

Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving your codebase, all while keeping it running the whole time.
Share This!Share on Google+Share on FacebookTweet about this on TwitterShare on RedditShare on LinkedIn

7 thoughts on “Simplifying Automated Form Generation, Validation, and Output

  1. Hi, Toby —

    I just took a short look at patForms from your link. The code appears to suffer some of the same drawbacks that I mention regarding HTML_QuickForm. Each element is an object unto itself. Also, it seems to combine the view aspects with the model/controller aspects, which I would prefer to keep separated.

    Your use of arrays in the PHP source here obviously predates the Savant and Solar_Form array. While I would quibble with the use of a string “yes” instead of a boolean “true”, the idea seems very much the same.

    Thanks for the comment. 🙂

  2. Would it be possible to only use the validation part of Solar/Forms? I recently cataloged an antipattern I call “All Web Development is New Development”. I just want to account for the possibility that the presentation is already set and I am not at liberty to alter it. But I might like to “bolt on” some validation to an existing form. This is all theoretical mind you 😉

  3. Hi, George — absolutely. The Solar_Form class is independent of model, and view, and controller. It’s just there to collect data (from any source) and let you do server-side validation of the form values. Thanks for asking. 🙂

  4. Hi again, George: looking at your question again, it seems you might be asking about only validation routines, as opposed to validation of form data via Solar_Form.

    The validation routines are actually part of Solar_Valid, and are called in sequence by Solar_Form::validate() based on the validations registered for each form element. Thus, tou can use Solar_Valid indpendently as well (it holds only static methods).

    Hope this helps.

  5. You might be interested to see ultimateForm, which has a similar simple array concept rather than the multiple-object system in QuickForm, which I’ve found over-complex myself.

    http://download.geog.cam.ac.uk/projects/ultimateform/

    See the example and its source code there, which seem to be quite similar to what you were talking about. It also has a templating mode and a data binding system for auto dbtable-to-widgets support.

    Solar_Form looks quite nice – I’ll definitely take a look at that.

Leave a Reply

Your email address will not be published. Required fields are marked *