Benjamin Schmaus

NAME

WWW::Form

SYNOPSIS

The WWW::Form module provides a simple framework for programming and dealing with HTML forms. It provides a simple API which allows developers to deal with web form programming quickly, flexibly, and consistently.

DESCRIPTION

This module:

  • provides functionality to handle all of the various types of HTML form inputs (this includes displaying HTML for the various form inputs)

  • handles populating form inputs with user entered data or progammer specified default values

  • provides robust validation of user entered input

  • handles presenting customizable error feedback to users

  • is easily extended, the Form module is designed to be easily inherited from, so you can easily add your own features. You might want to write a Form subclass called MultiStepForm, which might provide "wizard" like functionality, for example.

The most time consuming process (and it's not too bad) is creating the data structure used for instantiating a Form object. Once you have a Form object almost all your work is done, as it will have enough information to handle just about everything.

Before we get too involved in the details, let's take a look at a sample usage of the WWW::Form module in a typical setting. Note: If you're using Apache::Request and mod_perl then your code would look a little different, but not in how the Form module is used, however.

    #!/usr/bin/perl
    use strict;
    use warnings;

    use CGI;
    use WWW::Form;
    # used by WWW::Form to perform various
    # validations on user entered input
    use WWW::FieldValidator;

    # gets us access to the HTTP request data
    my $q = CGI->new();

    # hash ref of HTTP vars
    # would be $r->param() if you're using mod_perl
    my $params = $q->Vars();

    my $form;
    if ($params) {
        $form = WWW::Form->new(getFormFields(), $params);
    } else {
        $form = WWW::Form->new(getFormFields());
    }    

    # check to see that the form was submitted by the user
    # if you're using mod_perl, instead of $ENV{REQUEST_METHOD}
    # you'd have $r->method()
    if ($form->isSubmitted($ENV{REQUEST_METHOD})) {

        # validate user entered data
        $form->validateFields($params);

        # if the data was good, do something
        if ($form->isValid()) {
            # do some stuff with params because we know the
            # user entered data passed all of its
            # validation
        }
    }

    # display the HTML web page
    print <<HTML;
    Content-Type: text/html

    <html>
    <head>
    <title>A Simple HTML Form</title>
    </head>
    <body>
    HTML
        print "<form action='./form_test.pl' method='post'>\n";
        print "<table border='0' cellspacing='2' cellpadding='5'>\n";

        # print field labels, form inputs, and error feedback (if any)
        # in a table. if you're willing to display all of your form
        # content in a table then this method is all you should ever need,
        # if not, it's still easy to use the Form module to present 
        # your form content however you want
        print $form->getFieldHTMLRow('emailAddress');
        print $form->getFieldHTMLRow('password');

        print "</table>\n\n";
        print <<HTML;
    <input type="submit" value="Submit" />
    </form>
    </body>
    </html>
    HTML

    # returns data structure suitable for passing
    # to Form object constructor, the keys will
    # become the names of the HTML form inputs
    sub getFormFields {
        my %fields = (
            emailAddress => {
                label        => 'Email address',
                defaultValue => 'you@emailaddress.com',
                type         => 'text',
                validators   => [WWW::FieldValidator->new(
                                    WWW::FieldValidator::WELL_FORMED_EMAIL,
                                    'Make sure email address is well formed')]
            },
            password => {
                label        => 'Password',
                defaultValue => '',
                type         => 'password',
                validators   => [WWW::FieldValidator->new(
                                    WWW::FieldValidator::MIN_STR_LENGTH,
                                    'Password must be at least 6 characters', 6)]
            }
        );
        return \%fields;
    }

Instantiating A Form Object

As I said, instantiating a form object is the trickiest part. The Form constructor takes two paramteters. The first parameter called $fieldsData is a hash reference that describes how the form should be built. $fieldsData should be keyed with values that are suitable for using as the value of the form input's name HTML attribute. That is, if you call a key of your $fieldsData hash 'full_name', then you will have some type of form input whose name attribute will have the value 'full_name'. The values of the $fieldsData keys (i.e., $fieldsData->{$fieldName}) should also be hash references. This hash reference will be used to tell the Form module about your form input. All of these hash references will be structured similarly, however, there are a couple of variations to accommodate the various types of form inputs. The basic structure is as follows:

 {
   label => 'Your name', # UI presentable value that will label the form input
   defaultValue => 'Homer Simpson', # if set, the form input will be pre-populated with this value
   type => 'text', # the type of form input, i.e. text, checkbox, textarea, etc. (more on this later)
   validators => [] # an array of various validations that should be performed on the user entered input
 }  

So to create a Form object with one text box you would have the following data structure:

 my $fields = {
   emailAddress => {
     label        => 'Email address',
     defaultValue => 'you@emailaddress.com',
     type         => 'text',
     validators   => [WWW::FieldValidator->new(
                        WWW::FieldValidator::WELL_FORMED_EMAIL,
                       'Make sure email address is well formed')]
            }
     };

You could then say the following to create that Form object:

  my $form = Form->new($fields);

Now let's talk about the second parameter. If a form is submitted, then this second parameter should be used. It should be a hash reference of HTTP POST parameters. So if the previous form was submitted you would instantiate the Form object like so:

  my $params = $r->param(); # or $q->Vars if you're using CGI
  my $form   = Form->new($fields, $params);

At this point, let me briefly discuss how to specify validators for your form inputs.

The validators keys in the $fieldsData hash reference can be left empty, which means that the user entered input does not need to be validated at all, or it can take a comma separated list of WWW::FieldValidator objects. The basic format for a WWW::FieldValidator constructor is as follows:

  WWW::FieldValidator->new($validatorType,
                           $errorFeedbackIfFieldNotValid,
                           # optional, depends on type of validator
                           $otherVarThatDependsOnType,
                           # optional boolean, if input is 
                           # entered validation is run, 
                           # if nothing is entered input is OK
                           $isOptional)

The FieldValidator types are:

  WWW::FieldValidator::WELL_FORMED_EMAIL
  WWW::FieldValidator::MIN_STR_LENGTH
  WWW::FieldValidator::MAX_STR_LENGTH
  WWW::FieldValidator::REGEX_MATCH
  WWW::FieldValidator::USER_DEFINED_SUB

So to create a validator for a field that would make sure the input of said field was a minimum length, if any input was entered you would have:

  WWW::FieldValidator->new(WWW::FieldValidator::MIN_STR_LENGTH,
                           'Password must be at least 6 characters',
                           6, # input must be at least 6 chars
                           # input is only validated if user entered something
                           # if field left blank, it's OK
                           1)

How To Create All The Various Form Inputs

The following form input types are supported by the Form module (these values should be used for the 'type' key of your $fieldsData->{$fieldName} hash ref):

text password hidden checkbox radio select textarea

The following structure can be used for text, password, hidden, and textarea form inputs:

 $fieldName => {
   label => 'Your name',
   defaultValue => 'Homer Simpson',
   type => 'text',
   validators => []
 } 

The following structure should be used for radio and select form inputs:

The data structure for input types radio and select use an array of hash references called optionsGroup. The optionsGroup label is what will be displayed in the select box or beside the radio button, and the optionsGroup value is the value that will be in the hash of HTTP params depending on what the user selects. To pre-select a select box option or radio button, set its defaultValue to a value that is found in the optionsGroup hash ref. For example, if you wanted the option 'Blue' to be selected by default in the example below, you would set defaultValue to 'blue'.

 $fieldName => {
   label        => 'Favorite color',
   defaultValue => '',
   type         => 'select',
   optionsGroup => [{label => 'Green', value => 'green'},
                    {label => 'Red',   value => 'red'},
                    {label => 'Blue',  value => 'blue'}],
   validators   => []
 } 

The following structure should be used for checkboxes:

Note: All checkbox form inputs need a defaultValue to be specified, this is the value that will be used if the checkbox is checked when the form is submitted. If a checkbox is not checked then there will not be an entry for it in the hash of HTTP POST params. If defaultChecked is 1 the checkbox will be selected by default, if it is 0 it will not be selected by default.

 $fieldName => {
   label => 'Do you like spam>',
   defaultValue => 'Yes, I love it!',
   defaultChecked => 0, # 1 or 0
   type => 'checkbox',
   validators => []
 } 

Function Reference

The following section details the public API of the Form module.

NOTE: For style conscious developers all public methods are available using internalCapsStyle and underscore_separated_style. So 'isSubmitted' is also available as 'is_submitted', and 'getFieldHTMLRow' is also available as 'get_field_HTML_row', and so on and so forth.

For most cases the following 5 methods should be all you need: new, isSubmitted, validateFields, isValid, and getFieldHTMLRow.

new($fieldsData, $fieldsValues)

Creates a Form object. $fieldsData is a hash reference that describes your Form object. (See instantiating a Form object above.) $fieldsValues should be used when the form is submitted. The latter parameter has keys identical to $fieldsData. The $fieldsValues should be a hash reference of HTTP POST variables.

  Example:

  my $params = $r->param();
  my $form;
  if ($params) {
    $form = Form->new($fieldsData, $params);
  } else {
    $form = Form->new($fieldsData);
  }

isSubmitted($HTTPRequestMethod)

Returns true if the HTTP request method is POST. If for some reason you're using GET to submit a form then this method won't be of much help.

  Example:

  # returns true if HTTP method is POST
  $form->isSubmitted($r->method());

validateFields($params)

Returns hash reference of all the fields that are valid (generally you don't need to use this for anything though because if all the validation passes you can just use $params). Takes a hash reference of HTTP POST variables and validates their input according to the validators (WWW::FieldValidators) that were specified when the Form object was created. This will also set error feedback as necessary for form inputs that are not valid.

  Example:

  if ($form->isSubmitted($r->method)) {
    # validate fields because form was POSTed
    $form->validateFields($params);
  }

isValid()

Returns true is all form fields are valid or false otherwise.

  Example:

  if ($form->isSubmitted($r->method)) {
    # validate fields because form was POSTed
    $form->validateFields($params);

    # now check to see if form inputs are all valid
    if ($form->isValid()) {
        # do some stuff with $params because we know
        # the validation passed for all the form inputs
    }
  }

getFieldHTMLRow($fieldName, [$attributesString])

Returns HTML to display in a web page. $fieldName is a key of the $fieldsData hash that was used to create a Form object. $attributesString is an (optional) arbitrary string of HTML attribute key='value' pairs that you can use to add attributes to the form input.

The only caveat for using this method is that it must be called between <table> and </table> tags. It produces the following output:

  <!-- NOTE: The error feedback row(s) are only displayed if the field input was not valid -->
  <tr>
  <td colspan="2">$errorFeedback</td>
  </tr>
  <tr>
  <td>$fieldLabel</td>
  <td>$fieldFormInput</td>
  </tr>

  Example:

  $form->getFieldHTMLRow('name', " size='6' class='formField' ");

For more advanced form content handling the following methods can be used.

getFieldFeedbackHTML($fieldName)

Returns HTML error content for each vaildator belonging to $fieldName that doesn't pass validation. Returns HTML as so:

  <div class='feedback'>
  $validatorOneErrorFeedback
  </div>
  <div class='feedback'>
  $validatorTwoErrorFeedback
  </div>
  <div class='feedback'>
  $validatorNErrorFeedback
  </div>

Note: If you use this, you should implement a CSS class named 'feedback' that styles your error messages appropriately.

The following methods can be used to present form content any way you like.

getFieldFormInputHTML($fieldName, [$attributesString])

Returns an HTML form input for the specified $fieldName. $attributesString is an (optional) arbitrary string of HTML attribute key='value' pairs that you can use to add attributes to the form input, such as size='20' or onclick='someJSFunction()', and so forth.

getFieldLabel($fieldName)

Returns the label associated with the specified $fieldName.

getFieldErrorFeedback($fieldName)

Returns an array of all the error feedback (if any) for the specified $fieldName.

The next couple of methods are somewhat miscellaneous. They may be useful but in general you shouldn't need them.

getFieldValue($fieldName)

Returns the current value of the specified $fieldName.

setFieldValue($fieldName, $value)

Sets the value of the specified $fieldName to $value. You might use this if you need to convert a user entered value to some other value.

SEE ALSO

WWW::FieldValidator

AUTHOR

Ben Schmaus

BUGS

Known that I know of, but please let me know if you find any.

COPYRIGHT

Copyright 2003, Ben Schmaus. All Rights Reserved.

This program is free software. You may copy or redistribute it under the same terms as Perl itself. If you find this module useful, please let me know.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 930:

=cut found outside a pod block. Skipping to next block.