NAME
Apache::MVC - Web front end to a data source
SYNOPSIS
package BeerDB;
use base 'Apache::MVC';
sub handler { Apache::MVC::handler("BeerDB", @_) }
BeerDB->set_database("dbi:mysql:beerdb");
BeerDB->config->{uri_base} = "http://your.site/";
BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
# Now set up your database:
# has-a relationships
# untaint columns
1;
DESCRIPTION
A large number of web programming tasks follow the same sort of pattern: we have some data in a datasource, typically a relational database. We have a bunch of templates provided by web designers. We have a number of things we want to be able to do with the database - create, add, edit, delete records, view records, run searches, and so on. We have a web server which provides input from the user about what to do. Something in the middle takes the input, grabs the relevant rows from the database, performs the action, constructs a page, and spits it out.
This module aims to be the most generic and extensible "something in the middle".
An example would help explain this best. You need to add a product catalogue to a company's web site. Users need to list the products in various categories, view a page on each product with its photo and pricing information and so on, and there needs to be a back-end where sales staff can add new lines, change prices, and delete out of date records. So, you set up the database, provide some default templates for the designers to customize, and then write an Apache handler like this:
package ProductDatabase;
use base 'Apache::MVC';
__PACKAGE__->set_database("dbi:mysql:products");
BeerDB->config->{uri_base} = "http://your.site/catalogue/";
ProductDatabase::Product->has_a("category" => ProductDatabase::Category);
# ...
sub authenticate {
my ($self, $request) = @_;
return OK if $request->{ar}->get_remote_host() eq "sales.yourcorp.com";
return OK if $request->{action} =~ /^(view|list)$/;
return DECLINED;
}
1;
You then put the following in your Apache config:
<Location /catalogue>
SetHandler perl-script
PerlHandler ProductDatabase
</Location>
And copy the templates found in templates/factory into the catalogue/factory directory off the web root. When the designers get back to you with custom templates, they are to go in catalogue/custom. If you need to do override templates on a database-table-by-table basis, put the new template in catalogue/table.
This will automatically give you add
, edit
, list
, view
and delete
commands; for instance, a product list, go to
http://your.site/catalogue/product/list
For a full example, see the included "beer database" application.
HOW IT WORKS
There's some documentation for the workflow in Apache::MVC::Workflow, but the basic idea is that a URL part like product/list
gets translated into a call to ProductDatabase::Product->list
. This propagates the request with a set of objects from the database, and then calls the list
template; first, a product/list
template if it exists, then the custom/list
and finally factory/list
.
If there's another action you want the system to do, you need to either subclass the model class, and configure your class slightly differently:
package ProductDatabase::Model;
use base 'Apache::MVC::Model::CDBI';
sub supersearch :Exported {
my ($self, $request) = @_;
# Do stuff, get a bunch of objects back
$r->objects(\@objects);
$r->template("template_name");
}
ProductDatabase->config->{model_class} = "ProductDatabase::Model";
(The :Exported
attribute means that the method can be called via the URL /table/supersearch/...
.)
Alternatively, you can put the method directly into the specific model class for the table:
sub ProductDatabase::Product::supersearch :Exported { ... }
By default, the view class uses Template Toolkit as the template processor, and the model class uses Class::DBI
; it may help you to be familiar with these modules before going much further with this, although I expect there to be other subclasses for other templating systems and database abstraction layers as time goes on. The article at http://www.perl.com/pub/a/2003/07/15/nocode.html
is a great introduction to the process we're trying to automate.
AUTHOR
Simon Cozens, simon@cpan.org
LICENSE
You may distribute this code under the same terms as Perl itself.