use CGI qw(:standard);
print hidden("bacon");
To determine which page ("display product list", "display all items
in shopping cart", "confirm order") to display, use another hidden
field. We'll call this one .State so it won't
conflict with any field we might have called State
(for instance, in credit card billing information). To let the user
move from page to page, use submit buttons that set
.State to the name of the page to go to. For
instance, to make a button to take the user to the "Checkout" page,
use:
print submit(-NAME => ".State", -VALUE => "Checkout");
We wrap this in a function to make it easier to type:
sub to_page { return submit( -NAME => ".State", -VALUE => shift ) }
To decide what code to display, check the .State
parameter:
$page = param(".State") || "Default";
Put the code to generate each page in separate subroutines. You could
decide which subroutine to call with a long if ...
elsif ... elsif:
if ($page eq "Default") {
front_page( );
} elsif ($page eq "Checkout") {
checkout( );
} else {
no_such_page( ); # when we get a .State that doesn't exist
}
%States = (
'Default' => \&front_page,
'Shirt' => \&shirt,
'Sweater' => \&sweater,
'Checkout' => \&checkout,
'Card' => \&credit_card,
'Order' => \&order,
'Cancel' => \&front_page,
);
if ($States{$page}) {
$States{$page}->( ); # call the correct subroutine
} else {
no_such_page( );
}
Each page will have some persistent widgets. For instance, the page
that lets the user order t-shirts will want the number of t-shirts to
persist even when the user continues and orders shoes as well. We do
this by calling the page-generating subroutines with a parameter that
lets them know whether they're the active page. If they're not the
active page, they should only send back hidden fields for any
persistent data:
while (($state, $sub) = each %States) {
$sub->( $page eq $state );
}
The eq comparison returns true if the page is the
current page, false otherwise. The page-generating subroutine then
looks like this:
sub t_shirt {
my $active = shift;
unless ($active) {
print hidden("size"), hidden("color");
return;
}
print p("You want to buy a t-shirt?");
print p("Size: ", popup_menu('size', [ qw(XL L M S XS) ]));
print p("Color:", popup_menu('color', [ qw(Black White) ]));
print p( to_page("Shoes"), to_page("Checkout") );
}
Because the subroutines all generate HTML, we have to print the HTTP
header and start the HTML document and form before we call the
subroutines. This lets us print a standard header and footer for all
pages. Here, we assume we have subroutines
standard_header and
standard_footer for printing headers and footers:
print header("Program Title"), start_html( );
print standard_header( ), begin_form( );
while (($state, $sub) = each %States) {
$sub->( $page eq $state );
}
print standard_footer( ), end_form( ), end_html( );
Don't make the mistake of encoding prices in the forms. Calculate
prices based on the values of the hidden widgets, and sanity-check
the information where you can. For example, compare against known
products to make sure they're not trying to order a burgundy XXXXXXL
t-shirt. You can identify items by any string, whether it's a text
string like "sweater_xl_plain" that's a key to a
hash of prices, or whether you want to use a product "SKU" number
that you look up in an external database.