I have been working away on a module called CGI::FormManager for a
couple of months now (I registered it with CPAN a month or so ago and
gave a talk on it at the Birminham perl mongers meeting at the end of
October). The module is intended to render forms from definitions of
the forms and validate submitted data against those definitions. It is
inspired in part by CGI:FormBuilder, but delegates validation to
Data::FormValidator. I intend to use it with Catalyst and Template
Toolkit, but of course its application is wider than that.

Given that the module works with Catalyst I am making a first
announcement of version 0.04 here. The source code is available from:


Bear in mind that this is alpha-stage code -- I may change any aspect of
it at the moment, although I do have a client using an even earlier
version on a production web site.

I've attached an introduction to the module below.

I would welcome comments on the design and on any other aspect of it.


CGI::FormManager::Manual::Intro - introduction to the Form Manager

Coding for CGI forms is tedious. Often an application that accepts user
data will display an initial version of a form, check the user's data
then display another version of the form indicating errors in the data;
then when the data passes all validation checks it may display a
confirmation page containing the user's data, allowing the user to
confirm, cancel or go back and edit the data.

That is a lot of code - a lot of very tedious code. What I would prefer
to have is a definition of the structure of the form and have all the
code generated for me. This is what "CGI::FormManager" attempts to do --
it manages forms, validating and rendering them. It is inspired by
"Data::FormValidator" and "CGI::FormBuilder".

The module renders form with initial data, with data submitted by the
user, and will also render a confirmation form. It uses an internal
default templates render form using tables (this may change to CSS at
some stage). By default forms are laid out with the labels in one column
and the fields in the next column, but there are also facilities to deal
with repeating groups of elements (such as a checkout form), and I am
thinking of providing the facility to specify groups of fields (for
example for a billing address and a shipping address laid out side by

Validation is delegated to "Data::FormValidator", but the form
validation profile specified is augmented with information derived from
the definition of the fields making up the form.

NOTE: this module is still experimental and in a state of flux. I am
still playing with the API and the manner of declaring forms, and am
trying to get it all to "feel right".

Our example application is a fairly typical user registration
application with an initial data entry page and a data confirmation
page. The application uses Template Toolkit for rendering and Catalyst
as the application framework.

The HTML Templates
The template for the initial data entry page would look like:

<h1>Sample App</h1>

<p>Some instructions here.</p>
<p>Please fill in this form:</p>

[% form %]

Our Template Toolkit setup will wrap the output of the template in
"<html>" and "<body>" elements, and insert a "<head>" element and all
the other paraphernalia to give a complete HTML document.

The "form" variable is set up in the application code, which we will
come to later. It is a "CGI::FormManager::Form" object and as such
renders itself as HTML in a string context.

If there are errors in the data submitted then we go to the corrections

<h1>Sample App</h1>

<p>There were some problems with the data you entered</p>
<p>[% message %]</p>

[% form %]

Of course this fragment and the previous one are so similar that one
might want to roll them together. [EXPAND]

When we are happy with the data we display the confirmation form:

<h1>Sample App</h1>

<p>Thank you for entering your details. Please check the data
you entered and click confirm, edit or cancel,</p>

[% form.render_confirmation %]

The Form Definition
The form is defined in a hash passed to the "CGI::FormManager" package.
The hash contains three elements: "options", "elements" and

This is what our form definition might look like:

my $form_defn = {
options => { method => 'POST',
action => $url,
elements => [ '<h2>Your login credentials</h2>',
cfm_text_field( name => 'email',
label => 'Email address',
size => 40,
maxlength => 50,
constraint => email()
cfm_password_field( name => 'password',
cfm_password_field( name => 'password2',
label => 'Repeat your

'<h2>Personal Details</h2>',
cfm_text_field( name => 'first_name',

validation => { required => [ qw(email password) ],
constraint_methods => {
email => email(),

The "options" element is fairly self-explanatory, specifying the URL for
the form and that the form should be submitted with a POST request.

The "elements" element lists the "elements" that make up the form.
"CGI::FormManager" exports a set of subroutines for creating form
elements of different types; any plain text strings are regarded as a
literal text to be included in the form. Fields are named and may have a
label, which will be included in front of the field element in the HTML
document. If no label is specified then a label is created from the
field name by upper-casing the first letter and replacing underscores
with spaces. Constraints may be specified in the field constructors or
may be specified in the "validation" element.

The "validation" element specifies how the form should be validated. It
is augmented with information inferred from the field elements and
passed to "Data::FormValidator" when the form is validated.

The Application Code
The sample application is a Catalyst application, using a Template
Toolkit view component. See the Catalyst documentation for more

The main application package instantiates a "CGI::FormManager" object to
manage the forms (which are defined in the controller packages), storing
it in the application's configuration and then sets up the Catalyst

package MyRegApp;

use strict;
use Catalyst qw/-Debug/;

use CGI::FormManager;

MyRegApp->config( name => 'MyRegApp',
fmgr => CGI::FormManager->new() );

sub end : Global {
my ($self, $c) = @_;
$c->stash->{template} ||= "default.tt2";
$c->forward('MyRegApp::View::TT') unless $c->res->output;
die if $c->req->params->{die};


Registration is handled in a Catalyst controller package. It defines the
form and loads it into the form manager (which it picks out of the
application's configuration) as the package is loaded. Handling
registration requests is done in the "user_details" action routine.

package MyRegApp::Controller::Registration;

use strict;
use base 'Catalyst::Base';
use CGI::FormManager qw(:element_decls :constraint_methods);

my $form_defn = ...; # (defined above)

MyRegApp->config->{'fmgr'}->add_form(user_details => $form_defn);

sub default : Private {
my($self, $c) = @_;

sub user_details : Local {
my($self, $c) = @_;
my $form = $c->config->{fmgr}->form('user_details');
my $params = $c->request->params;
my $stash = $c->stash;

if (!keys %$params) {
$c->stash->{template} = "registration/new_user.tt2";
else {
$form = $form->validate($params);
if (!$form) {
$c->log->debug("validation failed" );
$c->log->debug(" missing:" . join(", ", $form->missing));
$c->log->debug(" invalid:" . join(", ", $form->invalid));
$c->stash->{template} = "registration/new_user_corrections.tt2";
$c->stash->{message} = <<EOS;
There are problems with the information you supplied,
please correct these and resubmit.
else {
$c->log->debug("validation passed");
if ($form->confirmed_data) {
# store the new user in the database
$c->stash->{template} = "registration/welcome.tt2";
else {
$c->stash->{template} = "registration/new_user_confirm.tt2";
$c->stash->{form} = $form;

The action method fetches the form object.

If there were no parameters then it is a new request so it sets the
template to be used to be the new user form page. Otherwise it gets the
form manager to validate the parameters supplied against the form

If validation fails then the corrections template is used, otherwise we
look and see if it was a confirmation form that was submitted. If we
have come from the confirmation page then create the new user and
display a welcome page, otherwise display the confirmation page.
(Actually this routine doesn't included code for handling the user
clicking on "edit" or "cancel" on the confirmation page, or the actual
user creation, but you get the idea.)

The general form of an action method is:

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

# Set up the template we are going to use (may be updated later)
$c->stash->{template} = 'template-name';

# Validate the submitted data against the form
my $results = $fmgr->validate(form1 => $c->request);

# Make the form-results object visible to the template
$c->stash->{form} = $results;

if ($results) {
# The data is valid (NB evaluating $results in a boolean
# context is the same as $results->success)

if ($results->confirmed_data) {
# do something with the data, according to whether
# the user clicked "confirm", "edit" or "cancel"
# (will need change the template used)
else {
# data is OK, might want the user to confirm
$c->stash->{template} = 'form1-confirm-template';
elsif ($results->submitted) {
# Data was received but is not valid -- do nothing and the
# form will be redisplayed with appropriate error messages.
# Note though that if $results->confirmed_data is true then
# there may be a problem -- the confirmation form was
# submitted but the data stored in hidden fields might
# have been modified maliciously.
else {
# no data was received so just display the initial form

Lookup Elements
These elements allow text to be generated by a callback routine.

Repeating Groups of Fields
Applications such as shopping carts require the ability to display
repeating groups of form elements. This is the syntax I have come up

cfm_repeating_group( elements =>
[ cfm_hidden_field ( name => 'prodcode' ),
cfm_lookup_element( name => 'desc',
header => 'Description',
value => \&_row_description ),
cfm_text_field ( name => 'qty',
header => 'Quantity'),
cfm_lookup_element( name => 'price',
value => \&_row_price ),
cfm_lookup_element( name => 'line_total',
value => \&_row_total,
footers => [ \&_sub_total,
\&_total ] )

This will be rendered as a table of four columns (the hidden field will
be prepended to the contents of the first column). The number of rows
will be determined from the form data and there will be four footer rows
for the calculated cells specified on the line total element.

This software should be considered alpha quality. It has not been
extensively tested and the exact details of the API have not been
finalized. However there are applications using it that are in
production use!

This is a partial list of outstanding issues.

* expand the documentation

* complete the initialization of form field objects (defaults, cross
population of validation/elements, radio field groups, etc)

* review the use of CSS classes on elements

* automatically generate JavaScript validation code (as
"CGI::FormBuilder" does)

* add objects to group elements -- for example for side-by-side layout
of billing and shipping addresses.

* expand the test suite

* design a proper Catalyst plugin

* have textual representation of form specification that can be read

Data::FormValidator, Template, CGI::FormBuilder

Andrew Ford <[email protected]>

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

Search Discussions

Discussion Posts

Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 1 of 8 | next ›
Discussion Overview
groupcatalyst @
categoriescatalyst, perl
postedNov 22, '05 at 1:12p
activeNov 22, '05 at 8:06p



site design / logo © 2023 Grokbase