Bill Moseley > Form-Processor-1.122970 > Form::Processor

Download:
Form-Processor-1.122970.tar.gz

Dependencies

Annotate this POD (2)

CPAN RT

New  1
Open  0
View/Report Bugs
Module Version: 1.122970   Source  

NAME ^

Form::Processor - validate and process form data

VERSION ^

version 1.122970

SYNOPSIS ^

In an application you might want a controller to handle creating and updating a "User" record. And not want to write much code. Here's using Catalyst as an example:

    package MyApplication::Controller::User;
    use strict;
    use MyApplication::Form::User;

    sub edit : Local {
        my ( $self, $c, $id ) = @_;

        # Create the form object
        my $form = MyApplication::Form::User->new( $id );


        # Update or create the user record if form posted and form validates
        $form->update_from_from( $c->request->parameters  )
            if $c->form_posted;


        $c->stash->{form} = $form;
    }

The above form class might then look like this:

    package MyApplication::Form::User;
    use strict;
    use base 'Form::Processor::Model::CDBI';

    sub object_class { 'DB::User' }

    sub profile {
        my $self = shift;

        return {
            required => {
                name        => 'Text',
                age         => 'PosInteger',
                sex         => 'Select',
                birthdate   => 'DateTimeDMYHM',
            },
            optional => {
                hobbies     => 'Multiple',
                address     => 'Text',
                city        => 'Text',
                state       => 'Select',
                email       => 'Email',
            },

            dependency => [
                [qw/ address city state /],
            ],
        };
    }

    sub options_sex {
        return (
            m => 'Male',
            f => 'Female',
        );
    }


    sub validate_age {
        my ( $self, $field ) = @_;
        $field->add_error('Sorry, you must be 18')
            if $field->value < 18;
    }

Or when you need a quick, small form do this in a controller:

    my @fields = qw/ first_name last_name email /;
    $c->stash->{form} = Form::Processor->new(
        profile => {
            required => {
                map { $_ => 'Text' } qw/ first_name last_name email /,
            },
        },
    );

DESCRIPTION ^

[Docs under construction. The docs are probably, well, less concise then they could be. Editors are welcome..]

Note: Please see HTML::FormHandler for a well-supported, Moose-based derivation of Form::Processor.

This is a class for working with forms. A form acts as a layer between your internal data representation (such as a database) and the outside world (such as a web form). Moving data between these areas often requires validation and encoding or expanding of the data. For example, a date might be a timestamp internally but externally is a collection of year, month, day, hour, minute input fields.

A form is made up of a collection of fields of possibly different types (e.g. Text, Email, Integer, Date), where the fields require validation before being accepted into their internal format. The validation process is really made up of a number of steps, where each step can be overridden to customize the process. See Form::Processor::Field for methods specific to fields.

Forms are (typically) defined by creating a separate Perl module that includes methods for defining the fields that make up the form, plus any special and additional validation checks on the fields.

Form::Processor does not generate any HTML. HTML should be generated in a "view" (and often using templates). And besides, HTML forms are trivial to create and in real life almost always needs customization. The use of a good template system makes this nearly painless.

Likewise, there is also no method to spit out an entire web form with a single method. Having a single method to generate a complete HTML form is often only useful for the most simple web forms.

This module is not restricted to use in a web environment, although that is the typical application. It was designed for use with Catalyst, Class::DBI, Template-Toolkit, and HTML::FillInForm. But, those are not required.

The design of this class is based a lot on the design of Rose::HTML::Objects, but, as mentioned, HTML widget generation is not part of the class. This class focuses more on moving data between the data store to the form that from the form to html. It's recommended that you look over Rose::HTML::Objects if not already done so.

The Form

As shown above in the synopsis, a "form" class is where a collection of "fields" are defined via a profile (that looks a lot like a Data::FormValidator profile). In general, the fields know how to validate input data, but the form class can also include additional validation methods for each field and can also cross-validate fields. The form class is what is used in your application code.

Fields

A form's "fields" are really small individual classes and they are often sub-classed to make more specific classes with additional constraints. For example, an Integer field might be a subclass of the basic Text field that limits input values to digits. And a year field might be a subclass of an Integer field that limits the range of integer values.

It's recommended that you create new field classes for each specific type of data you have. That is, create a "DeptNumber" field that knows what a department number will look like instead of using a generic "Text" field and then validating that in your form. Save field validation in the form for validation that can't be done in a generic way (like validating that the department number actually exists by doing a database lookup).

Unlike Rose::HTML::Objects, this class does not generate (x)html. I prefer to leave that up to the view (templates). But there is a plan to add that ability via a plug-in system for those that want it. I just find anything to do with HTML is better in the templates where it can be easily tweaked.

A method is provided to generate a hash of current values. This makes populating forms via HTML::FillInForm very easy. HTML::FillInForm is one of those modules that people either love or hate. I love it because HTML forms can be written in a very clean and generic way (i.e. no extra code needed to populate the form widgets). It also makes it easy to populate forms in a number of different ways in your application, which an be handy.

Compound Fields

Rose::HTML::Objects is really nice (you should take a look), and one of its features is it handles compound fields -- fields that are made up of other fields such as a collection of fields that are used to specify a date and time. This class doesn't have compound fields, but there's nothing stopping you from defining a field that is made up of a form that includes multiple fields. See Form::Processor::Field::DateTimeDMYHM for an example of this. After all, a field's job is to take input from something and create an internal value. So, its input can be another form made up of multiple fields.

To help with this there's a "name_prefix" form setting that can be used to help with nested forms.

A form's model class

The base class for your forms is Form::Processor, and Form::Processor can be used on its own. But, the fun is when used with a "form model class" -- a class that knows how to work with your data objects.

For example, the SYNOPSIS uses Form::Processor::Model::CDBI for working with CDBI objects. When Form::Processor::Model::CDBI is used then valid options for a field are automatically pulled from the database by looking at the relationships set up in the CDBI classes. When working with an field that "has_a" relationship with another table, then possible options can be fetched from the other table. These options can then be displayed in a HTML select list. And when validating input, the field can check that the input matches one of the available options.

As shown in the SYNOPSIS above, when using a form model class complete controllers can be written in two lines of code. Here's the first line:

    my $form = MyApplication::Form::User->new( $id );

That creates a form object. If $id is defined then the $id is fetched from the database for pre-populating the form. The fetched data object is stored in $form->item. A hash suitable for HTML::FillInForm is available in $form->fif (which can be used in a WRAPPER in Template-Toolkit or in the end() sub in Catalyst).

Then, the next line:

    $form->update_from_from( $c->request->parameters  )
        if $c->form_posted;

If a form was posted then call $form->update_from_form. That method validates the parameters and then updates (or creates) the object. Link tables are also updated (e.g. a user "has_many" roles using a mapping/link table).

In the template the fields can be fetched with form.field('name'). Fields have an error method to return the error(s) found during validation. Methods on the form object can be used to tell if validation has run or if an object was updated or created. See Methods below.

More on fields

Each form field is associated with a general type. The type name is used to load a module by that name:

    my $profile = {
        required => {
            title   => 'Text',
            age     => 'Integer',
        },
    };

Type "Text" loads the Form::Processor::Field::Text module and likewise, type 'Integer' loads Form::Processor::Field::Integer.

The most basic type is "Text" which takes a single scalar value. A "Select" class is similar, but its value must be a valid choice from a list of options. A "Multiple" type is like "Select" but it allows selecting more than one value at a time.

Each field has a "value" method, which is the field's internal value. This is the value your database object would have (e.g. scalar, boolean 0 or 1, DateTime object). A field's internal value is converted to the external value by use of the field's format_value() method. This method returns a hash which allows a single internal value to be made up of multiple fields externally. For example, a DateTime object internally might be formatted as a day, month, and year externally.

There's a form method called fif, that generates a hash of all the field's external values. This is quite useful for populating a form using HTML::FillInForm.

When data is passed in to validate the form, it is trimmed of leading and trailing whitespace by default and placed in the field's "input" attribute. Each field has a validate method that validates the input data and then moves it to the internal representation in the "value" attribute. Depending on the model, it's this internal value that is stored or used by your application.

By default, the validation is simply to copy the data from the "input" to the "value" field attribute, but you might have a field that must be converted from a text representation to an object (e.g. month, day, year to DateTime).

METHODS ^

These are the methods that can be called on a form object. See Form::Processor::Field for methods called on individual fields within a form.

name

Gets or set the form's name. This can be used to set the form's name when using multiple forms on the same page.

It's also prefixed to fields when asked for the field's id.

The default is form + a one to three digit random number.

    sub name { 'useform' }
profile

Returns the profile as a hashref as shown in the SYNOPSIS. This is the one method that you *must* override in your form class. This is what describes your form's fields, after all.

The profile provides a concise and easy way to define the fields in your form. Fields can also be added indiviually to a form, but using a profile is the recommended and common approach.

Fields typically fall into two major categories: required or optional. Therefore, the profile definition is grouped by those categories:

    my $profile => {
        required => {
            # required fields
        },
        optional => {
            # optional fields
        },
    };

The individual field names are the hash keys, and the field type is the value:

    my $profile = {
        required => {
            title   => 'Text',
            age     => 'Integer',
        },
    };

The field type maps directly to a field module, as described above. The values may optionally be a hash:

    my $profile = {
        required => {
            age     => {
                type    => 'Integer',
            },
        },
    };

The only required key is "type". Any other keys are considered method names and will be called on the field once created:

    my $profile = {
        required => {
            favorite_color => {
                type            => 'Select',
                label_column    => 'color_name',
                active_column   => 'is_active',
            },
        },
    };

Is basically:

    require Form::Processor::Field::Select;
    my $field = Form::Processor::Field::Select->new;
    $field->name( 'favorite_color' );
    $field->type( 'Select' );
    $field->form( $form );
    $field->required( 1 );
    $field->label_column( 'color_name' );
    $field->active_column( 'is_active' );
    $form->add_field( $field );

Possible profile keys

required

This points to a hash reference of field names as the keys and field types as the values. The field types are suffixes of the name space Form::Processor::Field:: and will be require()ed automatically. For example:

    sub profile {
        return {
            required => {
                first_name      => 'Text',
                roles           => 'Multiple',
            },
        };
    }

causes Form::Processor::Field::Text and Form::Processor::Field::Multiple to be loaded and calls their new() method. See Form::Processor::Field for more information on the field types.

As mentioned above, the value can optionally be a hash reference instead of a scalar. In this case the hash must contain a "type" key.

Each of these fields have their "required" attribute set true.

optional

Like above, but listed fields are not set as required.

    sub profile {
        return {
            required => {
                first_name      => 'Text',
                roles           => 'Multiple',
            },
            optional => {
                age             => 'Integer',
            }
        };
    }
auto_optional auto_required

This just make the above a bit easier.

This list an array of field names. The field types will try and be determined by various means (by calling $form->guess_field_type). For example, with Form::Processor::Model::CDBI it will look at the meta_info() to guess the field type.

    auto_required => [qw/ name age sex birthdate /],
    auto_optional => [qw/ hobbies address city state /],

With CDBI, if it has a has_many relationship with another CDBI object it will be a Select (pick one from a set of options), where a has_many relationship would be a Multiple select.

Other methods might be used such as asking the DBI layer for the column type information, or maybe via a method in your object classes that returns the type for each column.

The hope is that the Form::Processor::Model:: classes can get smart about determining the field type.

auto_all

*this method is not implemented*

If this is set then the value represents the method used to fetch all the field names from the object class.

    auto_all    => 'columns',  # for cdbi objects

This is not implemented yet, but something like:

    map { $_ => 'Auto' } $form->object_class->columns;
dependency

This is an array of arrays of field names. During validation if any of the fields in a given group are found to contain the pattern /\S/ then they are considered non-blank and then *all* of the fields in group are set to required. This should work like DFV's dependency_groups profile entry.

    sub profile {

        my @address_group       = qw/ address city state zip /;
        my @credit_card_group   = qw/ cc_no cc_expires /;
        return {
            required => {
                name    => 'Text',
                age     => 'Integer',
                date    => 'DateTimeDMYHM',
            },
            optional => {
                comment => 'Text',
                ...
            },
            dependency => [
                \@address_group,
                \@credit_card_group,
            ],
        };
    }

This class doesn't have DFV's "dependencies" option at this time.

unique

This is an array ref field names that should be unique in the data base. This feature depends on the model class being used.

new PARAMS

New creates a new form object. The constructor takes name/value pairs:

    MyForm->new(
        item    => $item,
        item_id => $item->id,
        verbose => 1
    );

Or, as is commonly done, only an item or item_id needs to be passed to the constructor. In this case a single parameter may be supplied:

    MyForm->new( $id );

or

    MyForm->new( $item );

If the value passed in is a reference then it is assumed to have and "id" method. So:

    MyForm->new( $item_object );

is the same as:

    MyForm->new(
        item    => $item_object,
        item_id => $item_object->id,
    );

The constructor can accept the following parameters:

item_id - the id of the object

The id (primary key) of the item (object) that the form is updating or has just created. The form's model class (e.g. Form::Processor::Model::CDBI) should have an init_item method that can fetch the object from the object_class for this id.

item - the object itself

An existing object (i.e. the object that id points to). This can be passed in to the new constructor, but typically it's loaded by the form's model class by its init_item method.

name

Name of the form. See the name method above.

name_prefix

Prefix used for all field names listed in profile when creating each field. This is useful for creating compound form fields where a single field is made up of a collection of fields. The collection of fields can be a complete form. An example might be a field that represents a DateTime object, but is made up of separate day, month, and year fields.

object_class

This defines the object class of the item (used by the form's model class to load, create, and update the object.

Typically, this would be defined as the "object_class" method in your form class, but can be specified in the constructor, for example, for small forms that do not use a form class (and specify the profile directly in the constructor -- see profile below).

profile

This is useful for very short forms where you do not wish to define a subclass for your form.

    my $form =  = Form::Processor::Model::CDBI->new(
        item            => $item,
        item_id         => $id,
        object_class    => $class,
        profile         => {
            required => {
                name    => 'Text',
                active  => 'Boolean',
            },
        },
    );
init_object

If init_object is supplied then it will be used instead of item to pre-populate the values in the form when init_from_object is called.

This can be useful when populating a form from default values stored in a similar but different object than the one the form is creating.

See init_from_object below.

The new() method will return false if the init() method returns false. Typically this would happen if passed in an invalid $id. You may override the init() method in your form class, but make sure you call

    return unless $self->SUPER::init(@_);

from your method.

load_form

This option is used to load form fields into memory. This can be called in a persistent environment such as mod_perl or FastCGI to pre-load modules.

This method is not called during normal use of the form.

This simply creates a dummy object and calls the method to load form fields via the profile. Any fields your form dynamically creates outside of of the form's profile method are not loaded. Options are not loaded as this may require reading from a data store which may not be available.

clear

Clears out state information on the form. Normally this does not need to be called by external code. An exception might be if the form stays in memory between uses -- but that's not the idea quite yet

init

This is called when the form object is first created. Parameters are passed unchanged from the new() call.

Returning false causes new() to return false.

As mentioned in new() above, if a single option is passed then it's considered as a "item" parameter if it's a reference, otherwise it's considered an "item_id".

If an "item_id" is passed in (either as a single parameter or as a named parameter) the init method will return false if the init_item method returns false. (Calling the item method when item is undefined automatically calls the init_item method.) The init_item method is typically defined in the form's model class and should know how to translate an item_id into an item object. See "init_item" below.

So, the idea is you can pass in an item_id into the constructor and have the init_item method validate the item_id and avoid validating the $id in the calling code (e.g. in a controller method).

    MyApp::Form->new( $id ) or return 'Invalid id supplied';

Note that if $id is undefined then new() will still return true. This allows the same code to be used for both create and update forms.

The init method calls the build_form method which reads the profile and creates the field objects. See that method for its magic.

The method init_from_object is called. This is typically specific to the type of form model used (e.g. CDBI) and is used to load each field's internal value from the object (which can then be used to populate the HTML form with $form->fif). init_from_object does nothing if no item_id (or item) is available. This would be the case when filling in a new blank form.

If an "init_object" is passed into the constructor then init_from_object will use this obeject (instead of "item") to load the initial field values. This is useful when initializing a form with values from another object.

Finally, the load_options method is called to load options for each field value used on multiple-choice fields. Typically, the form's model class will know how to load the options for each field by looking at the form's class relationships. See load_options below.

Again, this method will return false if an item_id is supplied and an item cannot be loaded from that id.

build_form

This parses the form profile and creates the individual field objects. It calls the make_field() method for each field. See the profile() method above for details on the profile format.

For "Auto" field types it calls the guess_field_type() method with the field name as a parameter. Form model classes will override guess_field_type(), or you can override in your own form class. You might do that if your field names are labeled with their type -- "event_time" "age_int", etc. Although that would be an odd thing to do.

The above can also return an array reference.

load_options

For all fields, if the field is a "Select" or "Multiple" (i.e. has an "options" method) then will call "options_$field_nane" if that method exists, otherwise will call the "lookup_options" method.

This should be called after $self->item is loaded because existing values may be needed in setting the valid options.

In general, "options_$field_name" would be defined in your form class, where "lookup_options" would be defined in the model form class and handle the more general case of looking up the available options in the database.

Here's an example of a method defined in your form's class to populate the "fruit" field with possible options:

    sub options_fruit {
        return (
            1 => 'Apple',
            2 => 'Grape',
            3 => 'Cherry',
        );
    }
dump_fields

Dumps the fields of the form. For debugging.

make_field
    $field = $form->make_field( $name, $type );

Maps the field type to a field class, and returns the field by calling new() on that field class.

The "$name" parameter is the field's name (e.g. first_name, age).

If the second parameter is a scalar it's taken as the field's type (e.g. Text, Integer, Multiple).

If the second parameter is a hash reference then the field type is determined from the required "type" value (i.e. $type-{type}> ).

The fields are assumed to be in the Form::Processor::Field name space. If you want to explicitly list the field's package prefix it with a plus sign:

    required => {
        name    => 'Text',  # Form::Processor::Field::Text
        foo     => '+My::Field::Foo',
    },
init_from_object

This method populates each field's value ($field->value) with either a scalar or an array ref from the object stored in $form->item. It does this by calling init_value() passing in the field object and $form->item. init_value() must return the value(s).

init_value() should be overridden in the form model subclass. For example, in ::Model::CDBI objects are expanded to primary keys for object methods that return a list of objects (e.g. has_many relationships).

If a method "init_value_$name" is found then that method is called instead. This allows overriding specific fields in your form class.

params

Returns a hash of parameters. The parameters are initialized from the item (see init_params() below), or are the last set of parameters passed to the validate() function.

See also the fif() method.

init_params

Calls each field's format_value() method to populate a parameters hash from each field's internal value. This is used to build a hash of all values in the form for use in populating the HTML form (using HTML::FillInForm). That has is returned by this method.

This method is called automatically when $form->params and params are not defiend. You may need to call this method directly if $form->item changes while the form object is in memory to force a refresh of params.

clear

Clears the internal and external values of the form

fif -- "fill in form"

Returns a hash of values suitable for use with HTML::FillInForm. It's a copy of $self->params with any passowrd fields removed.

sorted_fields

Calls fields and returns them in sorted order by their "order" value.

field NAME

Searches for field named "NAME". dies on not found. Useful for entering the wrong field.

    my $field = $form->field('first_name');

Pass a second true value to not die on errors.

exists

Returns true (the field) if the field exists

language_handle

Set or get the Locale::Maketext language handle. If not set will look for a language handle in the environment variable $ENV{LANGUAGE_HANDLE} and otherwise will Create a default language handler using the name space:

    Form::Processor::I18N

You can add your own language classes to this name space, but a more common use might be to provide an application-wide language handler.

The language handler can be passed in when creating your form instance or set after the object is created.

validate

Validates the form from the CGI parameters passed in. The parameters must be a hash ref with multiple values as array refs.

Returns false if validation fails.

Note that this returns the cached validated result if $form->ran_validation is true. So to force a re-validation call $form->clear. This should only happen if the $form object stays in memory between requests.

For each field:

    1) hash parameters are trimmed (override in field class) and
    saved to each field's "input" attribute.

    2) dependency fields are set by setting fields to required if needed

    3) validate_field is called for each field.  This tests that required
    fields are not blank, and that only fields marked as multiple can
    include multiple values.  For Selects and Multiple type fields the values
    must match existing options.

    If the above tests pass then the fields "validate" method is called.
    The validate method tests the input value (or values) and sets
    the field's input value based on the input data.

    The default validate method simply copies the input attribute to the
    value attribute:

        $field->value( $field->input );


    4) The form's validate_$fieldname is called, if the method exists AND
    if there's a value in the field.  Use cross_validate if you need to 
    validate fields that may be blank (such as setting defaults).

    5) The models validation method is called, if exists.  For example,
    this is used to check that a value is unique in the database.

Finally, after all fields have been processed:

    6) The form's cross_validate is called.  This allows access to all
    inflated values.  This is called even if not all fields validated.
    This just makes it easier to do bulk validation where fields may be
    in common.

If you override validate() make sure you set the flag fields like the validate here does.

cross_validate

This item can be overridden in the base class for the form. It's useful for cross checking *values* after they have been saved as their final validated value.

This method is called even if some fields did not validate.

has_error

Returns true if validate has been called and the form did not validate.

error_fields

Returns list of field with errors.

error_field_name

Returns the names of the fields with errors.

required_text

Returns either "required" or "optional" for the specified field.

Something like:

    <div class="[% field.required_text %]">
value

Short cut for:

    $form->field($name)->value;

Can pass a second true value to avoid die on not found.

value_changed

Returns true if the value in the item has changed from what is currently in the field's value.

This only does a string compare (arrays are sorted and joined). And note that:

    'foo' != ['foo']

which is probably incorrect.

use_existing_values

Default: false

By default all required fields MUST be supplied to the validate method. This works great for forms submitted by browsers because browsers submit all fields (except un-selected checkboxes and radio buttons).

When used with an API or AJAX it may be useful to allow a subset of fields to be submitted. Fields not submitted retain their existing value.

When this flag is true if the field is not supplied in the hash supplied to the validate method then will default to the field's current formatted value, if any.

Do not enable this on web forms, though. Otherwise, any checkboxs will always remain checked once checked.

If this feature is enabled it can be selectively disabled on a field by setting the field's "allow_existing" attribute. This simply means that a key of the field's name must be supplied. It does not inspect the key's value.

This is implemented by set_existing_values.

set_existing_values EXPERIMENTAL

This method will load existing values into params for any not supplied.

This does not work on compound fields.

uuid

Generates a hidden html field with a unique ID which the model class can use to check for duplicate form postings.

parent_field

This value can be used to link a sub-form to the parent field.

One way to create a compound field -- a field that is composed of other fields -- is by having the field include a form that is made up of fields. For example, a date field might be made up of a form that includes fields for the day, month, and year.

If a form has a parent_field associated with it then any errors will be pushed onto the parent_field instead of the current field. In the date example, an error in the year field will cause the error to be assigned to the date field, not directly on the year field.

This stores a weakened value.

CREATING A MODEL CLASS ^

Form model classes are used to moved form data between a database and the form, typically via an object relational mapping tool (ORM).

See Form::Processor::Model for details.

THINGS TO WONDER ABOUT ^

The CGI parameters passed in are stored in Form::Processor instead of in each field object.

When a field is entered and then changed to a different format, what format should be displayed? That is, a form with a date is updated. The text "tomorrow" is entered. If the form doesn't validate what should display? The actual formatted date for tomorrow, or still the text "tomorrow"?

Currently, if the form doesn't validate "tomorrow" is displayed. But if the form validates (and is updated by the model class) then the form will display the formatted date for tomorrow. That still may be different from what the date might look like next time it's fetched from the database (due to timezone settings). Another way to go would be to re-load from the database object to make the date look like it will next time it's fetched on a fresh form.

Init from object happens in Form::Processor, too. It would be nice to have each field know how to initalize from the source object. But, that doesn't work well with overriding Form::Processor with the Model class.

SUPPORT / WARRANTY ^

Form::Processor is free software and is provided WITHOUT WARRANTY OF ANY KIND. Users are expected to review software for fitness and usability.

AUTHOR ^

Bill Moseley <mods@hank.org>

COPYRIGHT AND LICENSE ^

This software is copyright (c) 2012 by Bill Moseley.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

syntax highlighting: