]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/Manual/Beer.pod
small quoting fix
[maypole.git] / lib / Maypole / Manual / Beer.pod
1 =head1 NAME
2
3 Maypole::Manual::Beer - The Beer Database, Twice
4
5 =head1 DESCRIPTION
6
7 We briefly introduced the "beer database" example in the
8 L<Introduction to Maypole|Maypole::Manual::About> chapter, where we
9 presented its driver class, C<BeerDB.pm>, as a fait accompli. Where
10 did all that code come from, and what does it actually mean?
11
12 =head2 The big beer problem
13
14 I have a seriously bad habit. This is not the beer problem; this is a
15 programming problem. The bad habit is that when I approach a problem I
16 want to solve, I get sidetracked deeper and deeper trying to solve more
17 and more generic problems, and then, satisfied with solving the generic
18 problem, I never get around to solving the specific problem. I always
19 write libraries for people writing libraries, and never write
20 applications.
21
22 The thing with really good beer is that it commands you to drink more of
23 it, and then by the morning you can't remember whether it was any good
24 or not. After buying several bottles of some random central African
25 lager on a dim recollection that it was really good and having it turn
26 out to be abysmal, this really became a problem. If only I could have a
27 database that I updated every time I buy a new beer, I'd be able to tell
28 whether or not I should buy that Lithuanian porter again or whether it
29 would be quicker just to flush my money down the toilet and cut out the
30 middle-man.
31
32 The only problem with databases on Unix is that there isn't really a
33 nice way to get data into them. There isn't really a Microsoft Access
34 equivalent which can put a simple forms-based front-end onto an
35 arbitrary database, and if there is, I either didn't like it or couldn't
36 find it, and after a few brews, you really don't want to be trying to type
37 in your tasting notes in raw SQL.
38
39 So you see a generic problem arising out of a specific problem here. I
40 didn't want to solve the specific problem of the beer database, because
41 I'd already had another idea for a database that needed a front-end. So
42 for two years, I sat on this great idea of having a database of tasting
43 notes for beer. I even bought that damned African beer again. Enough was
44 enough. I wrote Maypole.
45
46 =head2 The easy way
47
48 The first Maypole application was the beer database. We've already met
49 it; it looks like this.
50
51     package BeerDB;
52     use Maypole::Application;
53     BeerDB->setup("dbi:SQLite:t/beerdb.db");
54     BeerDB->config->uri_base("http://localhost/beerdb");
55     BeerDB->config->template_root("/path/to/templates");
56     BeerDB->config->rows_per_page(10);
57     BeerDB->config->display_tables([qw[beer brewery pub style]]);
58     BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
59     BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
60     BeerDB::Beer->untaint_columns(
61         printable => [qw/abv name price notes/],
62         integer => [qw/style brewery score/],
63         date => [ qw/date/],
64     );
65
66     use Class::DBI::Loader::Relationship;
67     BeerDB->config->{loader}->relationship($_) for (
68         "a brewery produces beers",
69         "a style defines beers",
70         "a pub has beers on handpumps");
71     1;
72
73 Now, we can split this into four sections. Let's look at them one
74 at a time. 
75
76 =head3 Driver setup
77
78 Here's the first section:
79
80     package BeerDB;
81     use Maypole::Application;
82     BeerDB->setup("dbi:SQLite:t/beerdb.db");
83
84 This is actually all you need for a functional database front-end. Everything
85 else is configuration details. This says three things: we're an application
86 called C<BeerDB>. This package is called the B<driver class>, because
87 it's a relatively small class which defines how the whole application is
88 going to run. 
89
90 The second line says that our front-end is going to be
91 L<Maypole::Application>, it automatically detects if you're using
92 mod_perl or CGI and loads everything necessary for you.
93
94 Thirdly we're going to need to set up our database with the given DBI
95 connection string. Now the core of Maypole itself doesn't know about
96 DBI; as we explained in the L<Model|Maypole::Manual::Model> chapter,
97 this argument is passed to our
98 model class wholesale. As we haven't said anything about a model
99 class, we get the default one, L<Maypole::Model::CDBI>, which takes a
100 DBI connect string. So this one line declares that we're using a C<CDBI>
101 model class and it sets up the database for us. In the same way, we
102 don't say that we want a particular view class, so we get the default
103 L<Maypole::View::TT>.
104
105 At this point, everything is in place; we have our driver class, it uses
106 a front-end, we have a model class and a view class, and we have a data
107 source.
108
109 =head3 Application configuration
110
111 The next of our four sections is the configuration for the application itself.
112
113     BeerDB->config->uri_base("http://localhost/beerdb");
114     BeerDB->config->template_root("/path/to/templates");
115     BeerDB->config->rows_per_page(10);
116     BeerDB->config->display_tables([qw[beer brewery pub style]]);
117
118 Maypole provides a method called C<config> which returns an object that
119 holds the application's whole configuration. We can use this to set some
120 parameters; the C<uri_base> is used as the canonical URL of the base
121 of this application, and Maypole uses it to construct links.
122
123 We also tell Maypole where we keep our template files, using
124 C<template_root>.
125
126 By defining C<rows_per_page>, we say that any listings we do with the
127 C<list> and C<search> templates should be arranged in sets of pages, with
128 a maximum of 10 items on each page. If we didn't declare that, C<list>
129 would try to put all the objects on one page, which could well be bad.
130
131 Finally, we declare which tables we want our Maypole front-end to
132 reference. If you remember from the schema, there's a table called
133 C<handpump> which acts as a linking table in a many-to-many relationship
134 between the C<pub> and C<beer> tables. As it's only a linking table, we
135 don't want people poking with it directly, so we exclude it from the
136 list of C<display_tables>.
137
138 =head3 Editability
139
140 The next section is the following set of lines:
141
142     BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
143     BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
144     BeerDB::Beer->untaint_columns(
145         printable => [qw/abv name price notes/],
146         integer => [qw/style brewery score/],
147         date => [ qw/date/],
148     );
149
150 As explained in the
151 L<Standard Templates|Maypole::Manual::StandardTemplates> chapter,
152 this is an set of instructions to
153 L<Class::DBI::FromCGI> regarding how the given columns should be edited.
154 If we didn't have this section, we'd be able to view and delete records,
155 but adding and editing them wouldn't work. It took me ages to work that
156 one out.
157
158 =head3 Relationships
159
160 Finally, we want to explain to Maypole how the various tables relate to
161 each other. This is done so that, for instance, when displaying a beer,
162 the brewery does not appear as an integer like "2" but as the name of
163 the brewery from the C<brewery> table with an ID of 2.
164
165 The usual L<Class::DBI> way to do this involves the C<has_a> and
166 C<has_many> methods, but I can never remember how to use them, so I came
167 up with the L<Class::DBI::Loader::Relationship> module; this was another
168 yak that needed shaving on the way to the beer database:
169
170     use Class::DBI::Loader::Relationship;
171     BeerDB->config->{loader}->relationship($_) for (
172         "a brewery produces beers",
173         "a style defines beers",
174         "a pub has beers on handpumps");
175     1;
176
177 C<CDBIL::Relationship> acts on a L<Class::DBI::Loader> object and
178 defines relationships between tables in a fairly free-form style.
179 The equivalent in ordinary C<Class::DBI> would be:
180
181        BeerDB::Brewery->has_many(beers => "BeerDB::Beer");
182        BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
183        BeerDB::Style->has_many(beers => "BeerDB::Beer");
184        BeerDB::Beer->has_a(style => "BeerDB::Style");
185
186        BeerDB::Handpump->has_a(beer => "BeerDB::Beer");
187        BeerDB::Handpump->has_a(pub => "BeerDB::Pub");
188        BeerDB::Pub->has_many(beers => [ 'BeerDB::Handpump' => 'beer' ]);
189        BeerDB::Beer->has_many(pubs => [ 'BeerDB::Handpump' => 'pub' ]);
190
191 Maypole's default templates will use this information to display, for
192 instance, a list of a brewery's beers on the brewery view page.
193
194 Note the quoting in 'BeerDB::Handpump' => 'beer', if you forget to quote the
195 left side when using strict you will get compilation errors.
196
197 This is the complete beer database application; Maypole's default templates
198 and the actions in the view class do the rest. But what if we want to do a
199 little more. How would we begin to extend this application?
200
201 =head2 The hard way
202
203 Maypole was written because I don't like writing more Perl code than is
204 necessary. I also don't like writing HTML. In fact, I don't really get
205 on this whole computer thing, to be honest. But we'll start by ways that
206 we can customize the beer application simply by adding methods or
207 changing properties of the Perl driver code.
208
209 The first thing we ought to look into is the names of the columns; most
210 of them are fine, but that "Abv" column stands out. I'd rather that was
211 "A.B.V.". Maypole uses the C<column_names> method to map between the
212 names of the columns in the database to the names it displays in the
213 default templates. This is provided by L<Maypole::Model::Base>, and
214 normally, it does a pretty good job; it turns C<model_number> into
215 "Model Number", for instance, but there was no way it could guess that
216 C<abv> was an abbreviation. Since it returns a hash, the easiest way
217 to correct it is to construct a hash consisting of the bits it got
218 right, and then override the bits it got wrong:
219
220     package BeerDB::Beer;
221     sub column_names { 
222         (shift->SUPER::column_names(), abv => "A.B.V.")
223     }
224
225 There's something to be aware of here: where are you going to type that
226 code? You can just put it in F<BeerDB.pm>. Perl will be happy with that,
227 though you might want to put an extra pair of braces around it to limit
228 the scope of that package declaration. Alternatively, you might think
229 it's neater to put it in a file called F<BeerDB/Beer.pm>, which is the
230 natural home for the package. This would certainly be a good idea if you
231 have a lot of other code to add to the C<BeerDB::Beer> package. But if
232 you do that, you will have to tell Perl to load the F<BeerDB/Beer.pm>
233 file by adding a line to F<BeerDB.pm>:
234
235     BeerDB::Beer->require;
236
237 For another example of customization, the order of columns is a bit
238 wonky. We can fix this by
239 overriding the C<display_columns> method; this is also a good way to
240 hide away any columns we don't want to have displayed, in the same way
241 as declaring the C<display_tables> configuration parameter let us hide
242 away tables we weren't using:
243
244     sub display_columns { 
245         ("name", "brewery", "style", "price", "score", "abv", "notes")
246     }
247
248 Hey, have you noticed that we haven't done anything with the
249 beers/handpumps/pubs thing yet? Good, I was hoping that you hadn't.
250 Anyway, this is because Maypole can't tell easily that a C<BeerDB::Beer>
251 object can call C<pubs> to get a list of pubs. Not yet, at least; we're
252 working on it. In the interim, we can explicitly tell Maypole which
253 accessors are related to the C<BeerDB::Beer> class like so:
254
255     sub related { "pubs" }
256
257 Now when we view a beer, we'll have a list of the pubs that it's on at.
258
259 =head2 Links
260
261 L<Contents|Maypole::Manual>,
262 Next L<The Request Cookbook|Maypole::Manual::Request>,
263 Previous L<Maypole's Request Workflow|Maypole::Manual::Workflow>
264