Документация
HTML CSS PHP PERL другое

Section 4.5.  Practical Uses in Web Applications

 
Previous
Table of Contents
Next

4.5. Practical Uses in Web Applications

One of the more popular ways of creating web-based applications these days is called the MVC Patternit's a design pattern where you have three components: a model of your data, a view that displays it, and a controller that routes requests and actions between the other two. It's a design pattern that first appeared in graphical applications in the Smalltalk programming language, but has translated reasonably well over to the Web. The key point of MVC is that, if you do it properly, your data model, your view, and your controller can be completely independent components, and you only need to worry about what goes on at the edges.

Now, the kind of templating system we looked at in the previous chapter looks very much like a view class: it abstracts out a way of presenting data. Similarly, the ways of treating database rows as objects look very much like model classes. Almost for free, using CPAN modules, we've got two of the three parts we need for a web application. The upshot is that, if you follow the MVC strategy, you have a very cheap way of writing web applications in which you delegate presentation to a templating library, you delegate data representation to an ORM library, and all you need to care about is what the darned thing actually does.

While this strategy can be applied to pretty much any of the tools we've talked about in the past two chapters, I want to look particularly at using Class::DBI and Template Toolkit; partly for the sake of example, partly because I personally think they fit together extremely well, and partly for another reason that will become apparent shortly.

4.5.1. Class::DBI and the Template Toolkit

The magic coupling of CDBI and TT, as they're affectionately known, was first popularized around 2001 by Tony Bowden, who'd just taken over maintaining Class::DBI. The idea spread through the mailing lists and Perl-mongers groups until, in 2003, Kate Pugh wrote a perl.com article (http://www.perl.com/lpt/a/2003/07/15/nocode.html) expounding the concept. Why? Because, as Pugh says, CDBI and TT work extremely well together.

Part of the reason for this is that, when templating database applications, you often want to display your objects and their attributes. Class::DBI allows you to get at their attributes by simple method calls, and Template Toolkit provides an easy way of making method calls in the templates. Your data goes straight from the database to the template without much need for any code in the middle.

For instance, for the simple job of viewing a CD, we can have a CGI script like so:

    use CD;
    use CGI qw/:standard/;
    use Template;
    print header();
    my $id = param("id");
    if (!$id) {
        print "<h1> You must supply an ID! </h1>"; exit;
    }
    my $obj = CD->retrieve($id);
    Template->new()->process("view.tt", { cd => $obj });

This takes the ID of a CD from the CGI form variables, retrieves the relevant CD, and passes it through to the template, which might look like this:


    <html>
       <head> <title>[% cd.name %]</title> </head>
    <body>
       <h1> [% cd.name %] </h1>
       <h2> [% cd.artist.name %] </h2>

    <ul>
    [% FOR track = cd.tracks %]
        <li> [% track.song.name %] </li>
    [% END %]
    </ul>
    </body>
    </html>

To view a list of CDs, we simply pass more objects to the template. However, if we want to avoid hitting the user's browser with the data on several hundred CDs, we can restrict the number of items on a page with Class::DBI::Pager:


    use CD;
    package CD;
    use Class::DBI::Pager;

    package main;
    use CGI qw/:standard/;
    use Template;
    print header();
    use constant ITEMS_PER_PAGE => 20;

    my $page = param("page") || 1;
    my $pager = CD->page(ITEMS_PER_PAGE, $page);
    my @cds = $pager->retrieve_all;
    Template->new()->process("view.tt", { cds => \@cds, pager => $pager });

Class::DBI::Pager is a mix-in for Class::DBI-based classes that allows you to ask for a particular page of data, given the number of items of data on a page and the page number you want. Calling page returns a Data::Page object that knows the first page, the last page, which items are on this page, and so on, and can be used in our template for navigation:

    [% IF pager.previous_page %]
    <A HREF="?page=[%pager.previous_page%]"> Previous page </A> |

    [% END %]
    Page [% pager.current_page %]
    [% IF pager.next_page %]
    | <A HREF="?page=[%pager.next_page%]"> Next page </A>
    [% END%]

The Class::DBI::FromCGI and Class::DBI::AsForm modules make it easy to construct forms for editing or creating records and then processing those changes in the database.

Of course, similar tricks can be done with templating languages other than 'Template Toolkit, such as HTML::Mason, but TT allows relatively complex constructs, such as method calls, without requiring the template writer to learn a fully fledged programming language. In an ideal world, the database can be handed off to a database team to populate, the templates given to web designers to create, and all that you as a programmer need to write are the kind of short scripts given above.

Or maybe even less ....

4.5.2. Maypole

At the beginning of 2004, a few ideas relating to CDBI and TT came together in my head, and I found myself writing lots of web applications that all did more or less the same sort of thingthey determined a set of CDBI objects to retrieve, got them out of a database, performed some action on them, and placed them into a template. I did what every good programmer should do on feeling that they've had to do something twiceI abstracted it out. The result was Maypole.

Maypole has two complementary but very distinct goals. Its first goal is to be a way of rapidly designing web applications by providing all the common code and templates for a standard frontend to a database: if you need a way to simply add, delete, view, and update records in a relational database, you can do it in no more than 20 lines of Perl code.

The second goal of Maypole is to be a generic controller method for all web applications. By default, it hooks into CDBI as a model class and TT as a template class to provide all the scaffolding code for a web application; all that you need to do is write the logic specific to what your application should do. And so the first goala web frontend to a databaseuses this generic controller with a load of metadata from the model class and a set of carefully designed default templates to produce an application that does the right thing.

Let's demonstrate Maypole by putting a quick frontend onto our Class::DBI record database. The code is simple enough:

    package CDPole;
    use base 'Maypole::Application';
    use CD;
    CDPole->config->model("Maypole::Model::CDBI::Plain");

    CDPole->setup([qw/ CD CD::Artist CD::Track /]);
    CDPole->config->uri_base("http://localhost/cdpole/");
    CDPole->config->template_root("/home/simon/modules/Apache-MVC/templates/");
    1;

We first say that we are based on Maypole::Application, a special class that determines whether this application should be CGI-based or Apache mod_perl-based, and sets up the inheritance appropriately. In our case, we're going to run this as a mod_perl application.

Next, we say that we're using a plain Class::DBI data source. If we didn't say this, Maypole would default to using Class::DBI::Loader to automatically read the tables from our data source. We also tell the application about the classesthat is, the tablesthat we want to use. Finally, we configure the application, telling it where it will live and where the templates are. With no change to the default templates, our application looks like Figure 4-2.

Figure 4-2. Viewing artists in Maypole


Of course, we don't always want to use the default templates; in fact, we should hardly ever use them, although they are useful for having something up and running quickly to interface to a database. Maypole allows us to override the templates in several ways. To understand these, we need to look at the basic principles of how Maypole works. Now we are moving from the first goal, the database interface, to the second goal, the application framework.

Maypole applications are made up of actions, which pull together some Perl code from the model side of the application with a template from the view side. The action we saw in the figure above was a list action on the artist class. Maypole, in effect, called CD::Artist->list( ) and put the results into a suitable list template. A more complicated action would be triggered by the URL http://localhost/cdpole/artist/edit/110. This would select artist ID 110 (Joni Mitchell), call CD::Artist->edit with that artist object as a parameter, and then find an edit template. We can view the whole Maypole process pictorially in Figure 4-3.

Figure 4-3. The Maypole work flow


To find the appropriate template, Maypole looks in three directories: first, a directory named after the table. So for /artist/edit/110, it would look for artist/edit. If this is not found, it looks in a directory specific to your application, which is called custom; that is, custom/edit. If again this is not found, Maypole falls back to the factory-supplied template in factory/edit.

As well as designing your own templates, you can also design your own actions by specifying that a particular class's method is exported and can be called from the web. This is done with the :Exported attribute:

    package CD::Artist;

    sub all_tracks :Exported {
        my ($self, $r, $artist) = @_;

        $r->template_args->{tracks} = [ map { $_->tracks } $artist->cds ]
    }

This method receives the Maypole request object and the artist object. We get a list of all the tracks on all the CDs that this artist has recorded and feed that to the template. The artist/all_tracks template might look like this:

    [% PROCESS macros %]
    [% INCLUDE header %]
    <h2> All tracks for [% artist.name %] </h2>

    <ul>
    [% FOR track = tracks %]
    <li> [% maybe_link_view(track) %] </li>
    [% END %]
    </ul>
    [% INCLUDE footer %]

That's all it takes to add a new action to the application. These are the basics of Maypole and enough to construct reasonably sophisticated web applications. Maypole has a full manual available at http://maypole.perl.org/.

We've seen Maypole in relation to Class::DBI and Template Toolkit, but its model and view classes are abstracted out such that you can use it with Alzabo or SPOPS, or with HTML::Mason or any other templating or database abstraction class you wish. This brings us onto the whole range of other application frameworks available for Perl.

4.5.3. Other Application Frameworks

Maypole is not the only player in the application framework space.

OpenInteract is Chris Winters's application framework using the SPOPS database abstraction layer. It's a fully featured framework, with session handling, LDAP support, authentication, groups, caching, cookies, and all sorts of other bits in the core.

PageKit is not tied to any object mapper, but it does require you to use either HTML::Template for your templates or XSLT.

OpenFrame has no relation to OpenInteract. It is something more than a web application framework; it works around the concept of pipelines, similar to the Maypole workflow we saw above, but in a much more generic way. Unlike the other tools, it doesn't provide any link with a data store; you have to code all that up yourself.

CGI::Application is an interesting idea that is parallel to these other kinds of application frameworks; it provides a way of reusing components of CGI applications (such as a package that provides a web-to-email form) so that you can recombine them in whatever way you want. It's another way of quickly creating web applications, but again it doesn't provide any MVC functionality or any direct link to a data store.

    Previous
    Table of Contents
    Next
    © 2000- NIV