http://www.perlmonks.org?node_id=199411

By Justin Simoni

I hope this little write up helps advocate both of the above modules and also helps anyone who'se beginning a similar project with a starting point.

Introduction

I recently got a call from a friend of mine, who was starting work on the web site design for a local kids clothing store. He asked me if I could do the backend for the shopping and I agreed. This friend is actually my brother's best friend. My brother is 30 and I am 21, which means I've known his friend since I was six and getting beat up by both of them. This would be actually the third time we've collaborated to create the online catalog and the shopping cart I wrote more than two years ago was getting old and rickety; cursed with the deadly disease of hacking on new features and not taking the time to fix the old bugs. Also to take in account that I've learned much about Perl and how to do these things in the past few years, it was time for the old cart to be taken down and a new one to be put in its place.

The Problem

Now, this is a local business and as such, had a local business budget. Usually "tight funds" and "Customized Shopping Cart" do not go hand in hand, but in things as critical as shopping carts, I do like to know what the underlying code is actually doing and I hate wading through source code that's not mine. My code may not be the absolute finest in the realms of Computer Science, but I trust it and I trust Perl.

I've also heard horror stories about Miva Merchant and, well not exactly horror stories, but more like GRIM WAR TALES about Miniven-Akopi-Interchange. Miva Merchant was a "drag in drop" feature of this particular hosting provider. I wasn't about to try to install Mini...Change.

So I had a problem that I didn't want to use an already created shopping cart, but I didn't know if rolling my own would fit the budget (and didn't want to absorb the costs of developing a new one)

I finally decided to roll my own when I got a bit of bad news. The company I've worked for 3 years which I started as an intern and at my peak, was the Principal Developer, somewhat dissolved.

Poof.

No more steady work. Now rolling my own shopping cart made sense, since I'd probably, well, definetly would be using it again with collaborations with the same friend, or even by myself.

Since I like to make things more complicated than they are and am never satisfied with one way to do things, I decided that the application itself would be written entirely in a different fashion than I was used to. I mean, what they hey, you know?

I did the development of this cart during a break in school. I'm, actually a Painting Major at a local Art School here in Denver. Why am I developing web-based applications? I don't know. But I am. It pays for paints and that is a good thing.

I immediately came into a problem about the design of the cart. When you work for a company for three years, you usually acquire quite a bit of company-owned code and resources that made your job oh-so-much more easier. This was indeed the case. In my career there, I created not one, not two, but three different shopping carts, the last being built upon a custom content management system, complete with so many bells and whistles (templating, templating language, database administration, makes coffee, washes your car, anything and everything, all in Perl, of course) I actually use this meta tool for all my last projects; I never started from square one, I started at the last step of the problem. And, it was good.

And it made me lazy.

Which is good in Perldom. I did not want again to start from scratch. I thought, "Gee, I wonder if I could build a similar CMS system using 'off the shelf' parts and have an environment that I'm more used to?" So, I did a bit of research.

Some Light

The very wise Markjugg has been talking about merits of CGI::Application and I've wanted to learn Template-Toolkit, which was similar to the templating language I developed. Now I had an excuse reason to try these out. Both of these modules do similar things from what I'm used to doing with my own code, but in slightly different ways. I was open to learn a new way, to get the job done. To get paid. Hmm, to hang out with all the cool art girls while reading Kerouac at hip, local coffee shops. Always keep your priorities in place.

Stumblings

CGI::Application has the idea of "Run-Modes"; each run mode is associated with both a screen of the application and a subroutine that does interesting things and also outputs this screen. The module makes you follow a strict design of one run-mode per screen. At first, this seemed limiting and hard to work with (or work around), since, well, I don't like strictness. A bit of a background:

I learned Perl by myself with the help of a few friends whom sort of put me in a loose apprentiship with them, where the exchange of ideas was easy and fundamental ideas could be hammered out.

I thoroughly thank them in my monkish meditations.

I also don't have a CS or Math background. I'm a Fine Arts major that goes to a school specifically for Art and Design. I learned Perl, maybe because Perl seemed like the perfect fit. I could express myself in code the same way I write down spontaneous prose or how I attach a painting. It fit me more that I had to fit it. Thanks, Larry.

When learning, I've made lots and lots of mistakes.

In almost every CGI application that I've created, I've used the following little snippet that nicely emulates CGI::Application's "Run-Mode" idea:

my %Mode = ( default => \&default, something => \&something, another => \&another ); if(exists($Mode{$q->param('mode')})) { $Mode{$q->param('mode')}->(); #call the correct subroutine }else{ &default; }

You might recognize this, since it's from the Perl Cookbook. So, you have a nice little switcher oo thingy up top of your script and the subroutines at the bottom. Works pretty darn well, thankyouverymuch.

The other major idea of CGI::Application is the idea that the actual logic behind your application, the actual anything of your application, lives in its own module, which used CGI::Application as it's base class. The script itself is usually no more than 5 lines, this is what my final shopping cart script looks like:

#!/usr/bin/perl -w use strict; use Cart::ShoppingCart; my $sc = Cart::ShoppingCart->new(); $sc->run();

That's voodoo right there.

When I first read the docs for CGI::Application, the step to make a module of the actual code and then to make this little script call the module seemed to be a funky way to complicate things. Why have two files that do the same as my one? Well, in the way I've been doing things, I actually have a CGI script that is alone 7200 lines long.

This isn't including any modules it calls. That's ridiculous for a CGI script and limits any sort of collaboration on the script. With the CGI::Application way, The script doesn't ever need to change, you can just swipe the module it calls. The module even has ways to pass it a config file so you can use the physical module in different instances all over the place. MarkJugg's Cascade does this now, if you'd like an example.

Once you learn how to fetch your CGI object:

my $q = $self->query();

instead of:

my $q = CGI->new;

and how to actually display the screen (just return the content as a string before exiting the run-mode subroutine), development of the application went by like lightning.

Templating

I actually started to play around with Template-Toolkit before creating the shopping cart and I didn't realize that CGI::Application had some hooks directly into HTML::Template, another, simpler templating system. I tried both and it really came into my environment on which one I chose. HTML::Template uses what looks like HTML comments to allow you to place variables that will later be switched out for what they represent.

This really didn't jive with me, since my editor of choice, BBEdit, shows comments as grayed out text. Template-Toolkit uses special thingies wrapped in square brackets and percent signs [% %] as it's template variables. In my editor, those showed prominently as black, while being surrounded by coded HTML. Sure, I could have just tweaked the prefs of BBEdit, but I know Template-Toolkit could do more things that might be of interest to me in the future. After all, I'm a very visual person and black is always in style.

Markjugg argued that Template-Toolkit also allowed you to do more than variable interpolation, but allowed you to also construct fairly complicated loop structures and even embed Perl code right in the template, which ruins the Content is Separated from Design ideal, which was the reason to go through the flaming hoops of CGI::Application and Template-Toolkit in the first place. I decided to still go with Template-Toolkit because even though I promised myself that I wouldn't use logic inside my templates, it was nice to know I could if I wanted to. In a crunch, I could put a little logic in, and polish later. It turns out I never even needed to do this.

As a templating system, I found Template-Toolkit to be very nice. It actually matches almost every feature that my own templating language had, except for how it bind with Perl variables, something mine lacked. After I figured out how this worked, my entire cart turned into simply, taking in information, saving it, and massaging variables I would then feed into Template-Toolkit to display. I cannot tell you how simple this all became. My add_to_cart run-mode looks a little bit like:

sub add_to_cart { my $self = shift; my $q = $self->query(); my $basket_id = $self->basket_id_from_cookie; my $inv = Cart::Inventory->new; my $basket = Cart::Basket->new(-basket_id => $basket_id); $basket->add_item(-basket_vars => {product_id => $q->param('i +d'), var_color => $q->param +('var_color'), var_size => $q->param +('var_size'), var_quantity => $q->param +('var_quantity'), }); $self->header_type('redirect'); $self->header_props(-cookie => $self->create_basket_cookie($baske +t_id), -uri => $C{URLS}->{cart_summary_url}); }

In this case, I didn't even display a screen, but changed the "header_type" to redirect the same as saying:

print $q->redirect(-uri => $some_url);

but in CGI::Application-speak.

The cart summary subroutine actually looks like this:

sub cart_summary { my $self = shift; my $q = $self->query(); my $html; my $basket_id = $self->basket_id_from_cookie; my $template = Template->new({INCLUDE_PATH => $C{TT_TEMPLATES}->{I +NCLUDE_PATH},}); my $vars->{cart} = $self->cart_vars; my $file = $C{TT_TEMPLATES}->{cart_summary}; $self->header_props(-cookie => $self->create_basket_cookie($basket +_id)); $template->process($file, $vars, \$html) || die "Template process +failed: ", $template->error(), "\n"; return $html; }

Basically, I'm creating an environment, populating the $vars hashref which will get fed into Template-Toolkit, and having Template-Toolkit output this information in the $html variable. You get Template-Toolkit to fill the $html variable with the parsed template information by passing a ref to a scalar in the process method, instead of nothing, which would then send the parsed template to STDOUT which is a huge no no in CGI::Application, but which would seem perfectly fine for me.

All the templates do is show the environment of the shopping cart and the order in a pretty way. This philosophy actually makes sense and works. The 15 line (which has all sorts of chances to get even tidier) subroutine allows me to have a screen that displays the entire contents of the cart. This is from someone who is known to produce 700 line subroutines. And paint 20 foot walls.

Of course, all the nitty gritty calculations and database stuff is handled by other modules, but having structure at the point of the actual application by having design completely out of any of the code and having a strict structure on how to design the actual application, allowed me to also keep all the other modules neat and tidy. It seems that one of my problems was that I would build a module around a script. This helped me grow out of that bad idea. Good foundation, good everything.

I found that I would use the actual templates as much as possible and my templates would almost be a list of [% INCLUDES %]. Observe the actual complete template for picking up completed orders by the merchant:

[% WRAPPER basic_admin_page.html %] <h1> Order# [% customer_info.customer_id %]</h1> <hr /> [% INCLUDE cart_summary_table.tt_html %] <hr /> [% INCLUDE billing_info_table.html %] [% INCLUDE cc_info_table.html %] &lt;div style="text-align:center"&gt; [% INCLUDE remove_button.html %] &lt;/div&gt; [% END %]
Template Toolkit seemed to be flexible enough where I could use the same template for similar chunks of HTML. Using just a bit of the logic, in the form of [% IF %] and [% FOREACH %] tt blocks was enough to make the template ultra flexible, without plopping major logic in the templates themselves. I find this so flexible, I'm basically going to base any web site I do on Template-Toolkit as the management tool. To someone whose first memories are LEGOS, it's absolutely second nature. I also decided that I didn't want Template-Toolkit tags (or as little as possible) in HTML files that I was editing in Dreamweaver, because, well, they look awful. Dreamweaver also likes to munge the Template-Toolkit style tags, switching some characters for their entities, so, I decided that any TT templates were off limit to Dreamweaver. Most of the HTML in the TT templates were easy enough for me to edit by hand anyways. In this situation, going with HTML::Template would have been smart, as HTML::Template tags look like HTML comments and are promptly ignored by Dreamweaver. To get around this, I would make a simple wrapper template that would just have this:
[% INCLUDE basic_page.html %]

The basic_page.html file would be a file managed by Dreamweaver and it's own templating system. Notice this is much of the same philosophy of CGI::Application. Dreamweaver MX also allows you to tell it what files it cannot touch, by the file endings. I choose 'tt_html' as what I would save my TT templates as, since all of them were snippets and would get munged the wrong way when managed in Dreamweaver MX. This time, I also told BBEdit that files ending in 'tt_html' should be treated as HTML files. Macs rock that way.

Other Fine Resources

To get past the problem of actually managing the Inventory of products, I just made a simple FileMaker database and had my friend fill it up. Filemaker exports in pretty clean CVS, which I fed into an online SQL database online, using Text::CSV and a little help from HTML::Entities to do the heavy lifting.

This allowed me to NOT have to make an interface to the online database. The CMS I made at work at an admin tool, basically for the admin tool, allowing you to build a database design. It took forever and is definetly cool. For testing and development, I also took advantage of Chris Hardie's DB BRowser, which allows you to manage multiple tables without much configuration to the script. Very nice.

The form validation in the shopping cart, and there are a lot of intricate forms in a shopping cart, were all easily handled using Data::FormValidator, which is maintained by Markjugg. This module really helps you almost skip the dull boring task of validating form input and is amazingly flexible.

When an order is placed, an email confirmation is sent. In this confirmation, the order itself is printed out. The problem is, that it's hard to make text-based tables without using Perl Formats, which have to be, as far as I can tell, directed to a file handle or STDOUT, which isn't too clean. I found that a little module called Text::FormatTable work wonders.

Conclusion

After just a little bit of learning, using CGI::Application and Template-Toolkit greatly speeds up development of web-based applications, even sophisticated ones, such as a shopping cart.

Total Development Time: 3 weeks.

Total Development Time For Previous Shopping Cart: 3 months.

The New shopping cart is up and humming and small bugs that have been found have been fixed easily. I have nothing but smiles and joy for both of these modules.

Edit kudra, 2002-09-20 Added a readmore tag

Replies are listed 'Best First'.
Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by grantm (Parson) on Sep 20, 2002 at 09:42 UTC

    ++skazat, nicely written piece.

    I also have found the Template Toolkit to be a fantastically useful tool and I've barely scratched the surface of its capabilities. I haven't really used DreamWeaver much but I have built templates which people to maintain using FrontPage. When I create my template in Perl I use:

    my $template = Template->new({ INCLUDE_PATH => '/some/path', START_TAG => '<%', END_TAG => '%>', });

    Which means that the templating tags look like ASP to FrontPage - I haven't had any trouble with things getting messed with so far.

      Ah,

      I like the start_tag and end_tag idea. For the templating language I developed, I used <?start and end?> which Dreamweaver recognized as server side code, and gave me a cute little PHP icon in the editor.

      I can't wait to use TT for producing XML, paint my paintings, make me dinner :)

       

      -justin simoni
      !skazat!

Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by derby (Abbot) on Sep 20, 2002 at 13:34 UTC

      If they Elders thus want to move this into the tutorials, my prayers will be with them. I didn't see "Tutorials" in the "Sections" nodlet, but there it is, in the "Information" nodlet.

      This is probably The first "Non Seekers" post I've ever done on Perl Monks. If you've read my previous post history, you'll see many posts asking about questions dealing with similar problem throughout the years.

      So thanks for everyone here for their help, a little bit of the application is in all of us :)

       

      -justin simoni
      !skazat!

Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by smitz (Chaplain) on Sep 20, 2002 at 10:10 UTC
    Great Article!

    Anyone else think this should go into Tutorials?
Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by Jazz (Curate) on Sep 20, 2002 at 22:41 UTC
Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by runrig (Abbot) on Sep 20, 2002 at 23:23 UTC
    When an order is placed, an email confirmation is sent. In this confirmation, the order itself is printed out. The problem is, that it's hard to make text-based tables without using Perl Formats, which have to be, as far as I can tell, directed to a file handle or STDOUT, which isn't too clean. I found that a little module called Text::FormatTable work wonders.

    Using $^A (see perlvar) and formline you don't need to direct format output to a handle. When I found out about them, I documented it here. Text::Format looks a bit more comprehensive anyway, though :-)

Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by disciple (Pilgrim) on Sep 21, 2002 at 18:47 UTC

    skazat++ Great job.

    Thanks so much for writing this up. I was going to post a question about what modules to use to create web applications. You have answered many questions for me.

    And, I too think it should be moved to tutorials. :-)

    -disciple
Re: My Experiences with using CGI::Application and Template::Toolkit To Build an Online Shopping Cart
by vek (Prior) on Sep 20, 2002 at 16:23 UTC
      I third smitz's motion to get this placed in tutorials, as I don't want to lose this information (I am going to build a cart for my mom's beading website someday in the near future and some of the resources used here could prove invaliable to me then).

      Great post, a definate ++, great resource. Nicely done!
      work it harder make it better do it faster makes us stronger more than ever hour after our work is never over.