Back on the submit of forms - with use case code
Posted: Wed Feb 25, 2009 9:57 pm
So I'm too used to dealing with SitePoint's own form handler which is great though there are some things i would change about it. I can't talk about the code though since it's not open source. Without going into detail it loads the view, parses the HTML and figures everything out from there. If validation fails it injects the form values back into the DOM so the user need to not fill them back out.
I've looked at Zend_Form and ugh... Basically I don't like anything that generates HTML for me to such an extent. I love structuring my forms using markup that makes perfect sense to me.
So I got thinking. How would *I* envisage form handling code working?
The Requirements
The Form
Real World Example from my Admin Area of the soon to be new swiftmailer.org. This is a form that I'll be using the register new downloads for consumption.

Pretty simple example. Just two fields.
The Markup (View concern, not Controller or Model)
I'd like to be able to write semantic markup *myself* like this.
Request Data Validation (Controller concern, though arguably grey area)
The validator need only check an array of "data" for correctness according to a set of rules. Those rules should work together to evaluate the validity of the data. The validator should not care where the data has come from... you just pass the data to it.
The Form Processing at a Controller Level
Like the validator, the form handler should only be working with request data (provided by the developer). It would be useful if it could filter values that are provided by the user and if it could have default values.
In this example the form handler sets some default values in the form based on the last time a download was added to the website (this is swiftmailer.org, so if Swift-4.0.0.tar.gz was added to GitHub last time, the form will be pre-filled with Swift-4.0.1.tar.gz).
In the above example $form->getValues() will use the defaults given if the user does not specify any. Validation is handled only if the form has been submitted and it's entirely separate from the form handler itself.
Ideally we'd like a way to add filters for the form data as it comes from $form->getValues(). Take for example a sites that has listings of data where people use crazy unicode characters like playing cards, filled squares and filled stars to draw attention to their listings but making your site look a complete mess so you'd like to strip those chars:
Making the HTML Form Value Pre-filling Work
So we've established that I want to use HTML to write my form. I don't want to use PHP code to write a form in PHP that generates a form in HTML... ugh.
Seems like I need a View helper that creates the attributes like selected="selected" and value="thing" in my elements...
So my HTML from above now becomes...
This view helper is told what type of field it's generating attributes for and also the details of the field name. For things like a <option> in a <select> it will generate either:
value="the-value" selected="selected"
Or the same but without the selected="selected" part.
The same sort of thing will happen for radio buttons and checkboxes, including those that accept multiple values.
I've got some of this stuff written already but I'm posting only the use case level stuff here for discussion. I'm curious if people share my thoughts in what form handling should constitute. In particular, Zend_Form seems to go way over-the-top.
I've looked at Zend_Form and ugh... Basically I don't like anything that generates HTML for me to such an extent. I love structuring my forms using markup that makes perfect sense to me.
So I got thinking. How would *I* envisage form handling code working?
The Requirements
- The form should be written in HTML by the user, not by some HTML-generator
- Validation should be separate, offered by a FormValidator class that uses strategies for Rules
- Fetching values should be done by getting them from the form handler, not directly from the request. This allows:
- Default values for request data
- Filtering of request data
- On a validation error, the HTML should be updated to include the submitted values (but only where the developer wants this)
- It should be really simple. Pure data handling, nothing super-amazing or over-engineered.
The Form
Real World Example from my Admin Area of the soon to be new swiftmailer.org. This is a form that I'll be using the register new downloads for consumption.

Pretty simple example. Just two fields.
The Markup (View concern, not Controller or Model)
I'd like to be able to write semantic markup *myself* like this.
Code: Select all
<form id="adddownloadform" action="" method="post"> <fieldset class="downloadfields"> <label for="field_filename"> <strong>Package Filename</strong> <span class="instruction"> You need to specify the full name of the file that is available for download </span> </label> <input type="text" class="text" name="filename" id="field_filename" value="" /> <label for="field_sources"> <strong>Download Location</strong> <span class="instruction"> Downloads must be hosted at a third-party site. The download manager will determine the correct link for the remote file. </span> </label> <fieldset id="field_sources" class="radios"> <label for="field_source_sourceforge"> <input type="radio" class="radio" name="source" id="field_source_sourceforge" value="sourceforge" /> SourceForge </label> <label for="field_source_googlecode"> <input type="radio" class="radio" name="source" id="field_source_googlecode" value="googlecode" /> Google Code </label> <label for="field_source_github"> <input type="radio" class="radio" name="source" id="field_source_github" value="github" /> GitHub </label> </fieldset> </fieldset> <fieldset class="buttons"> <input type="submit" class="primary button" name="add" value="Add Download" /> </fieldset> </form>The validator need only check an array of "data" for correctness according to a set of rules. Those rules should work together to evaluate the validity of the data. The validator should not care where the data has come from... you just pass the data to it.
Code: Select all
$validator = new FormValidator();
$validator->addRule(new Validation_Rules_RequiredRule(
//Field Field name Optional message
'filename', 'Filename', /*'{field} must be filled in'*/
));
$validator->addRule(new Validation_Rules_RequiredRule(
'source', 'Download Source', 'You must specify a download source'
));
$validator->addRule(new Validation_Rules_EnumRule(
'source', array('sourceforge', 'googlecode', 'github')
));
// .. snip ..
if (!$validator->isValid($requestDataArray)) {
//Data is not valid, do not process
}Like the validator, the form handler should only be working with request data (provided by the developer). It would be useful if it could filter values that are provided by the user and if it could have default values.
In this example the form handler sets some default values in the form based on the last time a download was added to the website (this is swiftmailer.org, so if Swift-4.0.0.tar.gz was added to GitHub last time, the form will be pre-filled with Swift-4.0.1.tar.gz).
Code: Select all
$form = new FormHandler(
//Request data
$this->getRequest()->getParams(),
//Handled fields
array('filename', 'source'),
//Request Method
FormHandler::METHOD_POST,
//Default Values
array(
'filename' => $this->_getNextVersionFilename(),
'source' => $this->_getLastUsedSource()
),
//Options if you want to change the way isSubmitted() and isCancelled() work
array(
'submit_field' => 'submit',
'cancel_field' => 'cancel'
)
);
// ... snip ...
if (!$form->isSubmitted()) {
//Don't process the form
}
if ($form->isCancelled()) {
//Perform logic to cancel processing rather than process input
}
//Get the form values from the form handler, not from the request
$values = $form->getValues();
if (!$validator->isValid($values)) {
//Form input not valid, don't process
}
/*** We can process the form data at the model-level now ***/Ideally we'd like a way to add filters for the form data as it comes from $form->getValues(). Take for example a sites that has listings of data where people use crazy unicode characters like playing cards, filled squares and filled stars to draw attention to their listings but making your site look a complete mess so you'd like to strip those chars:
Code: Select all
$form->addFilter('title', new Filters_Utf8NoiseFilter());So we've established that I want to use HTML to write my form. I don't want to use PHP code to write a form in PHP that generates a form in HTML... ugh.
Seems like I need a View helper that creates the attributes like selected="selected" and value="thing" in my elements...
Code: Select all
//Maybe it even just takes $form as input?
$viewHelper = new FormViewHelper($form->getValues());Code: Select all
<form id="adddownloadform" action="" method="post">
<fieldset class="downloadfields">
<label for="field_filename">
<strong>Package Filename</strong>
<span class="instruction">
You need to specify the full name of the file that is available for download
</span>
</label>
<input type="text" class="text" id="field_filename" <?php $this->formAttributes('input.text', 'filename'); ?> />
<label for="field_sources">
<strong>Download Location</strong>
<span class="instruction">
Downloads must be hosted at a third-party site. The download manager will determine
the correct link for the remote file.
</span>
</label>
<fieldset id="field_sources" class="radios">
<label for="field_source_sourceforge">
<input type="radio" class="radio" id="field_source_sourceforge" <?php $this->formAttributes('input.radio', 'source', 'sourceforge'); ?> />
SourceForge
</label>
<label for="field_source_googlecode">
<input type="radio" class="radio" id="field_source_googlecode" <?php $this->formAttributes('input.radio', 'source', 'googlecode'); ?> />
Google Code
</label>
<label for="field_source_github">
<input type="radio" class="radio" id="field_source_github" <?php $this->formAttributes('input.radio', 'source', 'github'); ?> />
GitHub
</label>
</fieldset>
</fieldset>
<fieldset class="buttons">
<input type="submit" class="primary button" name="add" value="Add Download" />
</fieldset>
</form>value="the-value" selected="selected"
Or the same but without the selected="selected" part.
The same sort of thing will happen for radio buttons and checkboxes, including those that accept multiple values.
I've got some of this stuff written already but I'm posting only the use case level stuff here for discussion. I'm curious if people share my thoughts in what form handling should constitute. In particular, Zend_Form seems to go way over-the-top.