]> git.decadent.org.uk Git - maypole.git/blob - lib/Apache/MVC.pm
Make this a proper subclass.
[maypole.git] / lib / Apache / MVC.pm
1 package Apache::MVC;
2 use base 'Maypole';
3 use Apache;
4 use Apache::Request;
5 use strict;
6 use warnings;
7 our $VERSION = "0.3";
8
9 sub get_request {
10     shift->{ar} = Apache::Request->new(Apache->request);
11 }
12
13 sub parse_location {
14     my $self = shift;
15     $self->{path} = $self->{ar}->uri;
16     my $loc = $self->{ar}->location;
17     $self->{path} =~ s/^$loc//; # I shouldn't need to do this?
18     $self->{path} ||= "frontpage";
19     my @pi = split /\//, $self->{path};
20     shift @pi while @pi and !$pi[0];
21     $self->{table} = shift @pi;
22     $self->{action} = shift @pi;
23     $self->{args} = \@pi;
24
25     $self->{params} = { $self->{ar}->content };
26     $self->{query}  = { $self->{ar}->args };
27 }
28
29 1;
30
31 =head1 NAME
32
33 Apache::MVC - Web front end to a data source
34
35 =head1 SYNOPSIS
36
37     package BeerDB;
38     use base 'Apache::MVC';
39     sub handler { Apache::MVC::handler("BeerDB", @_) }
40     BeerDB->set_database("dbi:mysql:beerdb");
41     BeerDB->config->{uri_base} = "http://your.site/";
42     BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
43     # Now set up your database:
44     # has-a relationships
45     # untaint columns
46
47     1;
48
49 =head1 DESCRIPTION
50
51 A large number of web programming tasks follow the same sort of pattern:
52 we have some data in a datasource, typically a relational database. We
53 have a bunch of templates provided by web designers. We have a number of
54 things we want to be able to do with the database - create, add, edit,
55 delete records, view records, run searches, and so on. We have a web
56 server which provides input from the user about what to do. Something in
57 the middle takes the input, grabs the relevant rows from the database,
58 performs the action, constructs a page, and spits it out.
59
60 This module aims to be the most generic and extensible "something in the
61 middle".
62
63 An example would help explain this best. You need to add a product
64 catalogue to a company's web site. Users need to list the products in
65 various categories, view a page on each product with its photo and
66 pricing information and so on, and there needs to be a back-end where
67 sales staff can add new lines, change prices, and delete out of date
68 records. So, you set up the database, provide some default templates
69 for the designers to customize, and then write an Apache handler like
70 this:
71
72     package ProductDatabase;
73     use base 'Apache::MVC';
74     __PACKAGE__->set_database("dbi:mysql:products");
75     BeerDB->config->{uri_base} = "http://your.site/catalogue/";
76     ProductDatabase::Product->has_a("category" => ProductDatabase::Category); 
77     # ...
78
79     sub authenticate {
80         my ($self, $request) = @_;
81         return OK if $request->{ar}->get_remote_host() eq "sales.yourcorp.com";
82         return OK if $request->{action} =~ /^(view|list)$/;
83         return DECLINED;
84     }
85     1;
86
87 You then put the following in your Apache config:
88
89     <Location /catalogue>
90         SetHandler perl-script
91         PerlHandler ProductDatabase
92     </Location>
93
94 And copy the templates found in F<templates/factory> into the
95 F<catalogue/factory> directory off the web root. When the designers get
96 back to you with custom templates, they are to go in
97 F<catalogue/custom>. If you need to do override templates on a
98 database-table-by-table basis, put the new template in
99 F<catalogue/I<table>>. 
100
101 This will automatically give you C<add>, C<edit>, C<list>, C<view> and
102 C<delete> commands; for instance, a product list, go to 
103
104     http://your.site/catalogue/product/list
105
106 For a full example, see the included "beer database" application.
107
108 =head1 HOW IT WORKS
109
110 There's some documentation for the workflow in L<Apache::MVC::Workflow>,
111 but the basic idea is that a URL part like C<product/list> gets
112 translated into a call to C<ProductDatabase::Product-E<gt>list>. This
113 propagates the request with a set of objects from the database, and then 
114 calls the C<list> template; first, a C<product/list> template if it
115 exists, then the C<custom/list> and finally C<factory/list>. 
116
117 If there's another action you want the system to do, you need to either
118 subclass the model class, and configure your class slightly differently:
119
120     package ProductDatabase::Model;
121     use base 'Apache::MVC::Model::CDBI';
122
123     sub supersearch :Exported {
124         my ($self, $request) = @_;
125         # Do stuff, get a bunch of objects back
126         $r->objects(\@objects);
127         $r->template("template_name");
128     }
129
130     ProductDatabase->config->{model_class} = "ProductDatabase::Model";
131
132 (The C<:Exported> attribute means that the method can be called via the
133 URL C</I<table>/supersearch/...>.)
134
135 Alternatively, you can put the method directly into the specific model
136 class for the table:
137
138     sub ProductDatabase::Product::supersearch :Exported { ... }
139
140 By default, the view class uses Template Toolkit as the template
141 processor, and the model class uses C<Class::DBI>; it may help you to be
142 familiar with these modules before going much further with this,
143 although I expect there to be other subclasses for other templating
144 systems and database abstraction layers as time goes on. The article at
145 C<http://www.perl.com/pub/a/2003/07/15/nocode.html> is a great
146 introduction to the process we're trying to automate.
147
148 =head1 AUTHOR
149
150 Simon Cozens, C<simon@cpan.org>
151
152 =head1 LICENSE
153
154 You may distribute this code under the same terms as Perl itself.