NAME

Class::DBI::FormBuilder - Class::DBI/CGI::FormBuilder integration

SYNOPSIS

package Film;
use strict;
use warnings;

use base 'Class::DBI';
use Class::DBI::FormBuilder;

# for automatic validation setup
use Class::DBI::Plugin::Type;

# POST all forms to server
Film->form_builder_defaults( { method => 'post' } );

# These fields must always be submitted for create/update routines
Film->columns( Required => qw( foo bar ) );

# same thing, differently
# Film->form_builder_defaults->{required} = [ qw( foo bar ) ];


# In a nearby piece of code...

my $film = Film->retrieve( $id ); 
print $film->as_form( params => $q )->render;   # or $r if mod_perl

# For a search app:    
my $search_form = Film->search_form;            # as_form plus a few tweaks


# A fairly complete app:

my $form = Film->as_form( params => $q );       # or $r if mod_perl

if ( $form->submitted and $form->validate )
{
    # whatever you need:
    
    my $obj = Film->create_from_form( $form );
    my $obj = Film->update_from_form( $form );              
    my $obj = Film->update_or_create_from_form( $form );    
    my $obj = Film->retrieve_from_form( $form );
    
    my $iter = Film->search_from_form( $form );
    my $iter = Film->search_like_from_form( $form );
    my $iter = Film->search_where_from_form( $form );
    
    my $obj = Film->find_or_create_from_form( $form );
    my $obj = Film->retrieve_or_create_from_form( $form );
    
    print $form->confirm;
}
else
{
    print $form->render;
}

# See CGI::FormBuilder docs and website for lots more information.

DESCRIPTION

This module creates a CGI::FormBuilder form from a CDBI class or object. If from an object, it populates the form fields with the object's values.

Column metadata and CDBI relationships are analyzed and the fields of the form are modified accordingly. For instance, MySQL enum and set columns are configured as select, radiobutton or checkbox widgets as appropriate, and appropriate widgets are built for has_a, has_many and might_have relationships. Further relationships can be added by subclassing.

A demonstration app (using Maypole::FormBuilder) can be viewed at

http://beerfb.riverside-cms.co.uk

METHODS

All the methods described here are exported into the caller's namespace, except for the form modifiers (see below).

Form generating methods

form_builder_defaults( %args )

Stores default arguments for the call to CGI::FormBuilder::new().

as_form( %args )

Builds a CGI::FormBuilder form representing the class or object.

Takes default arguments from form_builder_defaults.

The optional hash of arguments is the same as for CGI::FormBuilder::new(), and will override any keys in form_builder_defaults.

Note that parameter merging is likely to become more sophisticated in future releases (probably copying the argument merging code from CGI::FormBuilder itself).

It's impossible to know whether pk data are expected in the submitted data or not. For instance, while processing a form submission:

my $form = My::Class->as_form;

my $obj = My::Class->retrieve_from_form( $form );       # needs pk data
my $obj = My::Class->find_or_create_from_form( $form ); # does not

pk hidden fields are always present in rendered forms, but may be empty (submits undef). undef does not pass validation tests. The solution is to place pk fields in 'keepextras', not in 'fields'. That means they are not validated at all. The only (I think) place pk data are used is in retrieve_from_form

as_form_with_related2
search_form( %args )

Build a form with inputs that can be fed to search methods (e.g. search_where_from_form). For instance, all selects are multiple, and fields that normally would be required are not.

In many cases, you will want to design your own search form, perhaps only searching on a subset of the available columns. Note that you can acheive that by specifying

fields => [ qw( only these fields ) ]

in the args.

The following search options are available. They are only relevant if processing via search_where_from_form.

search_opt_cmp

Allow the user to select a comparison operator by passing an arrayref:

search_opt_cmp => [ ( '=', '!=', '<', '<=', '>', '>=', 
                      'LIKE', 'NOT LIKE', 'REGEXP', 'NOT REGEXP',
                      'REGEXP BINARY', 'NOT REGEXP BINARY',
                      ) ]

Or, transparently set the search operator in a hidden field:

search_opt_cmp => 'LIKE'
search_opt_order_by

If true, will generate a widget to select (possibly multiple) columns to order the results by, with an ASC and DESC option for each column.

If set to an arrayref, will use that to build the widget.

# order by any columns
search_opt_order_by => 1

# or just offer a few
search_opt_order_by => [ 'foo', 'foo DESC', 'bar' ]

Form modifiers

These methods use CDBI's knowledge about its columns and table relationships to tweak the form to better represent a CDBI object or class. They can be overridden if you have better knowledge than CDBI does. For instance, form_options only knows how to figure out select-type columns for MySQL databases.

You can handle new relationship types by subclassing, and writing suitable form_* methods (e.g. form_many_many). Your custom methods will be automatically called on the relevant fields.

form_hidden

Ensures primary column fields are included in the form (even if they were not included in the fields list), and hides them.

form_options

Identifies column types that should be represented as select, radiobutton or checkbox widgets. Currently only works for MySQL enum columns.

There is a simple patch for Class::DBI::mysql that enables this for MySQL set columns - see http://rt.cpan.org/NoAuth/Bug.html?id=12971

Patches are welcome for similar column types in other RDBMS's.

Note that you can easily emulate a MySQL enum column by setting the validation for the column to an arrayref of values. Haven't poked around yet to see how easily a set column can be emulated.

form_file

Unimplemented - at the moment, you need to set the field type to file manually.

Figures out if a column contains file data.

form_has_a

Populates a select-type widget with entries representing related objects. Makes the field required.

Note that this list will be very long if there are lots of rows in the related table. You may need to override this method in that case. For instance, overriding with a no-op will result in a standard text type input widget.

This method assumes the primary key is a single column - patches welcome.

Retrieves every row and creates an object for it - not good for large tables.

form_has_many

Also assumes a single primary column.

form_might_have

Also assumes a single primary column.

Form handling methods

Note: if you want to use this module alongside Class::DBI::FromForm, load the module like so

use Class::DBI::FormBuilder BePoliteToFromForm => 1;

and the following 2 methods will instead be imported as create_from_fb and update_from_fb.

You might want to do this if you have more complex validation requirements than CGI::FormBuilder provides.

All these methods check the form like this

return unless $fb->submitted && $fb->validate;

which allows you to say things like

print Film->update_from_form( $form ) ? $form->confirm : $form->render;

That's pretty concise!

create_from_form( $form )

Creates and returns a new object.

update_from_form( $form )

Updates an existing CDBI object.

If called on an object, will update that object.

If called on a class, will first retrieve the relevant object (via retrieve_from_form).

update_or_create_from_form

Class method.

Attempts to look up an object (using primary key data submitted in the form) and update it.

If none exists (or if no values for primary keys are supplied), a new object is created.

Search methods

Note that search methods (except for retrieve_from_form) will return a CDBI iterator in scalar context, and a (possibly empty) list of objects in list context.

All the search methods except retrieve_from_form require that the submitted form should either be built using search_form (not as_form), or should supply all required (including has_a) fields. Otherwise they may fail validation checks for missing required fields.

retrieve_from_form

Use primary key data in a form to retrieve a single object.

search_from_form

Lookup by column values.

search_like_from_form

Allows wildcard searches (% or _).

Note that the submitted form should be built using search_form, not as_form.

search_where_from_form

Class::DBI::AbstractSearch must be loaded in your CDBI class for this to work.

If no search terms are specified, then the search

WHERE 1 = 1

is executed (returns all rows), no matter what search operator may have been selected.

find_or_create_from_form

Does a find_or_create using submitted form data.

retrieve_or_create_from_form

Attempts to look up an object. If none exists, a new object is created.

This is similar to update_or_create_from_form, except that this method will not update pre-existing objects.

Automatic validation setup

If you place a normal CGI::FormBuilder validation spec in $class->form_builder_defaults->{validate}, that spec will be used to configure validation.

If there is no spec in $class->form_builder_defaults->{validate}, then validation will be configured automatically. The default configuration is pretty basic, but you can modify it by placing settings in $class->form_builder_defaults->{auto_validate}.

You must load Class::DBI::Plugin::Type in your class if using automatic validation.

Basic auto-validation

Given no validation options for a column in the auto_validate slot, the settings for most columns will be taken from %Class::DBI::FormBuilder::ValidMap. This maps SQL column types (as supplied by Class::DBI::Plugin::Type) to the CGI::FormBuilder validation settings VALUE, INT, or NUM.

MySQL ENUM or SET columns will be set up to validate that the submitted value(s) match the allowed values (although SET column functionality requires the patch to CDBI::mysql mentioned above).

Any column listed in $class->form_builder_defaults->{options} will be set to validate those values.

Advanced auto-validation

The following settings can be placed in $class->form_builder_defaults->{auto_validate}.

validate

Specify validate types for specific columns:

validate => { username   => [qw(nate jim bob)],
              first_name => '/^\w+$/',    # note the 
              last_name  => '/^\w+$/',    # single quotes!
              email      => 'EMAIL',
              password   => \&check_password,
              confirm_password => {
                  javascript => '== form.password.value',
                  perl       => 'eq $form->field("password")'
              }
                      

This option takes the same settings as the validate option to CGI::FormBuilder::new() (i.e. the same as would otherwise go in $class->form_builder_defaults->{validate}). Settings here override any others.

skip_columns

List of columns that will not be validated:

skip_columns => [ qw( secret_stuff internal_data ) ]
match_columns

Use regular expressions matching groups of columns to specify validation:

match_columns => { qr/(^(widget|burger)_size$/ => [ qw( small medium large ) ],
                   qr/^count_.+$/             => 'INT',
                   }
                   
validate_types

Validate according to SQL data types:

validate_types => { date => \&my_date_checker,
                   }
                   

Defaults are taken from the package global %TypesMap.

match_types

Use a regular expression to map SQL data types to validation types:

match_types => { qr(date) => \&my_date_checker,
                 }
                 
debug

Control how much detail to report (via warn) during setup. Set to 1 for brief info, and 2 for a list of each column's validation setting.

strict

If set to 1, will die if a validation setting cannot be determined for any column. Default is to issue warnings and not validate these column(s).

Validating relationships

Although it would be possible to retrieve the IDs of all objects for a related column and use these to set up validation, this would rapidly become unwieldy for larger tables. Default validation will probably be acceptable in most cases, as the column type will usually be some kind of integer.

timestamp

The default behaviour is to skip validating timestamp columns. A warning will be issued if the debug parameter is set to 2.

Failures

The default mapping of column types to validation types is set in %Class::DBI::FormBulder::ValidMap, and is probably incomplete. If you come across any failures, you can add suitable entries to the hash before calling as_form. However, please email me with any failures so the hash can be updated for everyone.

TODO

Better merging of attributes. For instance, it'd be nice to set some field attributes (e.g. size or type) in form_builder_defaults, and not lose them when the fields list is generated and added to %args.

Store CDBI errors somewhere on the form. For instance, if update_from_form fails because no object could be retrieved using the form data.

Detect binary data and build a file upload widget.

is_a relationships.

enum and set equivalent column types in other dbs.

Think about non-CDBI has_a inflation/deflation. In particular, maybe there's a Better Way than subclassing to add form_* methods. For instance, adding a date-picker widget to deal with DateTime objects.

Figure out how to build a form for a related column when starting from a class, not an object (pointed out by Peter Speltz). E.g.

my $related = $object->some_col;

print $related->as_form->render;

will not work if $object is a class. Have a look at Maypole::Model::CDBI::related_class.

Integrate fields from a related class object into the same form (e.g. show address fields in a person form, where person has_a address).

AUTHOR

David Baird, <cpan@riverside-cms.co.uk>

BUGS

Please report any bugs or feature requests to bug-class-dbi-plugin-formbuilder@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-DBI-Plugin-FormBuilder. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

COPYRIGHT & LICENSE

Copyright 2005 David Baird, All Rights Reserved.

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