NAME
CGI::State - Converts CGI parameters into a multi-dimensional hash
SYNOPSIS
First you make your HTML form to submit to your CGI script. For example:
<form action="order.cgi">
<input type="text" name="Contact.first_name" value="Dan" />
<input type="text" name="Contact.email" value="dan@mealtips.com" />
<input type="hidden" name="item[0].price" value="10.00" />
<input type="hidden" name="item[0].description" value="Widget" />
<input type="submit" value="Order Now!" />
</form>
Notice the names of the hidden and text fields? Keep this in mind. Then you create a CGI script to receive the form variables after they're submitted:
#!/usr/bin/perl -wT
use strict;
use CGI;
use CGI::State;
use Data::Dumper;
#Simulate receiving CGI parameters
my $cgi = CGI->new;
#Un-Flatten the data structure
my $state = CGI::State->state( $cgi );
#Show us what the $state hashref now looks like...
print $cgi->header('text/plain'), Data::Dumper->new([$state], [qw(state)])->Indent(3)->Quotekeys(0)->Dump;
#Which would print out the following data structure:
$state = {
Contact => {
first_name => 'Dan',
email => 'dan@mealtips.com'
},
item => [
#0
{
description => 'Widget',
price => '10.00'
}
]
};
#Also, you can change $state back into the original query string:
$cgi = CGI::State->cgi( $state );
print $cgi->query_string;
#Which would print out: (in no particular order)
Contact.first_name=Dan&Contact.email=dan@mealtips.com&item[0].price=10.00&item[0].description=Widget
DESCRIPTION
This module takes incoming CGI form variables, and transforms them into a multi-dimensional data structure. It can recreate a hash of hashes, a hash of lists, a hash of lists of hashes etc, any number of levels deep.
A limitation of CGI is it's inability to naturally group together submitted variables. For example, you can't have someone fill in an order form and have all their contact and item information grouped separate from each other in a perl data structure without specifically doing this in the perl code.
This module was originally written because I always hated receiving CGI parameters, putting them into a hash, and have this hash contain 20 or more elements. I think it's messy, and very tedious writing code to group related CGI parameters together. I wanted parameters to be put into a multi-dimensional data structure automatically for me.
METHODS
- $state = CGI::State->state( $cgi )
-
This routine takes one argument, a CGI.pm object reference.
It will return a hashref containing as many levels as specified in the input parameters.
It allows you to logically group together form elements, so that when the CGI script receives them, it has to do no logic of it's own to group things together.
This routine cycles through all the form variables and looks for the following format:
$object_name[$index].$attribute <-- Multivalued OR $object_name.$attribute <-- Single valued ...And translates them into the following: $hashref = { $object_name => [ #$index = 0 { $attribute => $value }, #$index = 1 { $attribute => $value }, #... ], }
It also puts all non-objects into the top-level of the hashref.
The format I choose to describe the data structure closely resembles Javascript's style. I know, I know. But the syntax is close to perl, and should be fairly simple for most perl programmers to pick up. In the future I may change this module to allow you to specify different delimeters, to allow more perl-like syntax.
- $cgi = CGI::State->cgi( $state )
-
This routine takes one argument, a multi-dimensional hash.
It will return a flattened CGI object based on the values referenced by $state. Very useful for maintaining state across various CGI invocations.
EXAMPLES
One major advantage to grouping parameters together in a multi-dimensional data structure is that you have everything map into your database cleanly.
For example, let's say that we have a relational table called Contact, which stores information about a customer. Inside this table there are three columns called first_name, last_name, and email.
Imagine there is a form where customer information is collected, such as the following:
<form action="save_customer.cgi">
<input type="text" name="Contact.first_name" />
<input type="text" name="Contact.last_name" />
<input type="text" name="Contact.email" />
</form>
When this form is submitted, we create a CGI.pm object to capture the data, then pass this object off to CGI::State->state, which returns a hash reference:
my $cgi = CGI->new; #Create the CGI Object
my $state = CGI::State( $cgi ); #$state is a hash reference
Assuming that we submit the form, the hash reference would look like this, as shown by Data::Dumper:
$state = {
Contact => {
first_name => 'Dan',
last_name => 'Kubb'
email => 'dan@mealtips.com'
},
};
With this structure, it would be rather easy just to pass off $state->{Contact} to a subroutine that inserts Contact information into a database. There's no sorting, grouping or hard-coding the column names anywhere in your code! I am a firm believer that the database table and column names should dictate the HTML form parameter names, and perl hash element names. This module helps enforce that and make it easier to write code that will "map" HTML forms into a database with minimal effort.
LIMITATIONS
Having the ability to manipulate the data with simple HTML can either be seen as a benefit or a liability. I find it a benefit, but I can imagine that others might not see it that way, especially when non-programmers are responsible for constructing all web forms.
Anything you expect to be a hash key should not be a number.
TODO
Add more security measures and error checking.
SEE ALSO
AUTHOR
Copyright 2001, Dan Kubb <dan@mealtips.com>
This module is distributed under the same terms as Perl itself. Feel free to use, modify and redistribute it as long as you retain the correct attribution.