Back on the submit of forms - with use case code

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Back on the submit of forms - with use case code

Post by Chris Corbyn »

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 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.
Does anybody disagree with any of my ideas on what form handling really comes down to? There's fetching data, resetting the form fields, and validation of data. All separate problems IMO.

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.

Image

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>
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.

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
}
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).

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 ***/
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:

Code: Select all

$form->addFilter('title', new Filters_Utf8NoiseFilter());
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...

Code: Select all

//Maybe it even just takes $form as input?
$viewHelper = new FormViewHelper($form->getValues());
So my HTML from above now becomes...

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>
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.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Back on the submit of forms - with use case code

Post by Benjamin »

Hey Chris, I'm curious if you have looked at how symfony generates forms and your opinions of that.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Back on the submit of forms - with use case code

Post by Chris Corbyn »

astions wrote:Hey Chris, I'm curious if you have looked at how symfony generates forms and your opinions of that.
I have since I used to use symfony and again, I didn't use its form view helpers. symfony uses standard functions for view helpers which is fine, but why generate an input tag generated when it's so easy to just write the damn thing? All it does is removes flexibility and introduces complexity.

From what I remember symfony has functions like this (with different parameters since my memory is hazy):

Code: Select all

<fieldset>
  <?php input_tag('name_of_tag', 'default value', 'className', array($styleAttributes)); ?>
</fieldset>
symfony has validation integrated into the controllers too which I can sort of understand (the action in broken down into "validate", "action" and "error" methods).

What I'm trying to get at is that "generating forms" should not happen at all (at least in my mind). You generate a form by writing HTML. That HTML can be pretty damn sexy too, but that's the developer to decide, not some form-generator.

Everybody seems to want to avoid writing HTML for forms. But writing HTML is exactly what you have to do if you want well structured forms and really, it's not that hard with good CSS and proper use of form elements. If you use a consistent style then it's easy enough to copy/paste & modify across your entire site.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Back on the submit of forms - with use case code

Post by alex.barylski »

This is essentially how I deal with form's in my own framework, only the Form object is is passed values via request object.

Code: Select all

// Render a form from a template
$fname = $request->get('fname');
 
$form = new Form('some.action.php');
 
$form->loadTemplate('step.one.xml');
$form->setField('fname', $fname); // Set the FORM field whatever value $fname is set to
 
echo $form->executeTemplate();
 
// Validate form is done by other controller which uses my own validation library
 
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Back on the submit of forms - with use case code

Post by Benjamin »

It's a bit more elegant then what you remember I believe. I am currently studying symfony and while the form framework may not be perfect, it seems to be flexible enough.

Have a look and let me know what you think...

http://www.symfony-project.org/jobeet/1_2/Propel/en/10
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Back on the submit of forms - with use case code

Post by Chris Corbyn »

PCSpectra wrote:This is essentially how I deal with form's in my own framework, only the Form object is is passed values via request object.

Code: Select all

// Render a form from a template
$fname = $request->get('fname');
 
$form = new Form('some.action.php');
 
$form->loadTemplate('step.one.xml');
$form->setField('fname', $fname); // Set the FORM field whatever value $fname is set to
 
echo $form->executeTemplate();
 
// Validate form is done by other controller which uses my own validation library
 
So that's the view. But what about validation and data-handling?

What you're doing looks similar to SitePoint's approach (loading a template and parsing it).
astions wrote:It's a bit more elegant then what you remember I believe. I am currently studying symfony and while the form framework may not be perfect, it seems to be flexible enough.

Have a look and let me know what you think...

http://www.symfony-project.org/jobeet/1_2/Propel/en/10
Again, where's the HTML for the form? it's all generated by the framework, which to me is not very flexible. Imagine something like an "Advanced Search" form that has a non-linear layout, these things just get tangled really quickly.

I only scanned over the documentation there (they've changed things since I last used symfony) but from what I see it looks very similar to Zend_Form.

I'm more interested in working with the data than working with the HTML. I think that's where my opinion differs from these other solutions (and apparently from other developers).
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Back on the submit of forms - with use case code

Post by Benjamin »

The html is actually a table you create in a view. The form is then rows in the table. So this allows you to style the form as you wish while having the creation automated at the same time. I'm certain there has to be a way to specify the number of columns etc.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Back on the submit of forms - with use case code

Post by Chris Corbyn »

astions wrote:The html is actually a table you create in a view. The form is then rows in the table. So this allows you to style the form as you wish while having the creation automated at the same time. I'm certain there has to be a way to specify the number of columns etc.
It's rare that I find using a table to create a form is the most semantic thing to do. Forms should be composed of <fieldset> elements with <label> elements.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Re: Back on the submit of forms - with use case code

Post by Benjamin »

That's cool. I'm all about finding the best solution. I'll be following this thread for sure.
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: Back on the submit of forms - with use case code

Post by matthijs »

I agree with what you propose. There's Rules and Filters for the validation and filtering. The controller handles only the form processing logic. And the html for the form is written as plain html in a template. Form values and errors should be pulled from the form handler.

For really simple forms I could see the logic of using a form generator, but in almost all cases I don't see the advantage. Big drawbacks with form generation for me are:
- I already know how to write html. Now I have to learn a new "form-generating" language, specific to the framework. Easy to make mistakes and I can't reuse that knowledge elsewhere
- There's always little details that aren't possible with form generation or at least are difficult to achieve.

About the validation rules: what we try to do in the skeleton code is make it possible for the form handler to get rules from different sources. So instead of hardcoding rules in the controller, it should be possible for the form handler to ask the model for the rules it needs. The idea behind this is that the model already knows what rules there are for each data field. So it doesn't make sense to repeat those rules in each and every controller/form.
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Back on the submit of forms - with use case code

Post by inghamn »

Why have form handlers at all, really? Forms are an HTML formatting thing. But as far as the controllers are concerned, data sent in the request can come in a whole variety of ways. Controllers really only need to know how to handle input from the user. It doesn't matter whether the user filled out an HTML for, or submitted an AJAX request, or put key/values into the URL.

Controllers should have a set of inputs that they know how to handle. Once an application has any complexity, this set of inputs no longer matches cleanly to the HTML forms displayed in the browser, nor should they.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Back on the submit of forms - with use case code

Post by Chris Corbyn »

I was thinking that myself too. It's more of a RequestHandler than a FormHandler. The only things that make my code closer to forms than other request inputs are the fact I have isSubmitted() and isCancelled() methods. The View helper is very much tied to forms, but that's since it's view level stuff.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Back on the submit of forms - with use case code

Post by alex.barylski »

So that's the view. But what about validation and data-handling?
In my own applications there are two controllers, one which builds the view, like I show above, the other which handles the request(s).
controller1()
{
// Insert code above
}

controller2()
{
$model = new Model();
$model->createUser($request->get('email')); // Model handles validation and handling
}
My model(s) then use my framework validation functions. For that reason I couldn't implement a form library that didn't anything more than parse a template and inject values into form fields. If the form library explicitly handled form validation, I would struggle with which to use when a form was submitted. Do I use the framework validation routines (which are quite simple/primitive) or something more advanced like a form specific validation library that understands every type of data imaginable (ie: Address, postal, etc).

I don't often check data integrity to that degree, except on email (I make their RFC) but if it's requested or required I just add the extra test inside the model itself. My validation library does nothing more than check the value, lengths and basic formats using regex.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Back on the submit of forms - with use case code

Post by Chris Corbyn »

Yeah same here. Mostly I'm just checking that a field is submitted or loosely that it's in the required format. Any other sort of validation such as "does this record you're trying to insert already exist?" should happen in the model.

Your split controller approach sounds strange. I've never seen that before.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Back on the submit of forms - with use case code

Post by alex.barylski »

Your split controller approach sounds strange. I've never seen that before
I think my example was somewhat misleading.

In web site applications, my framework is slightly different than the web application version (same framework -- just different routing). In the web application a controller is a class and an action is the method -- only one method per controller. In the web site version a controller is a class and an action is a method -- many methods allowed.

I further divide or organize my controllers into panel/transput (input/ouput) and actions.

The transput controllers are responsible for firing up required models, initializing the view(s) and returning the results to a response. All 'actions' are handled by an 'action' controller and then redirected or forwarded onto the appropriate transput controller upon success or failure.
Post Reply