]> git.decadent.org.uk Git - maypole.git/blob - doc/StandardTemplates.pod
Can't be arsed to do any more for now.
[maypole.git] / doc / StandardTemplates.pod
1
2 =head1 Maypole's Standard Templates and Actions
3
4 As we saw in our CRUD example, Maypole does all it can to make your life
5 easier; this inclues providing a set of default actions and
6 factory-supplied templates. These are written in such a generic way,
7 making extensive use of class metadata, that they are more or less
8 applicable to any table or application. However, in order to progress
9 from automatically generated CRUD applications to real customized
10 applications, we need to begin by understanding how these default
11 actions do their stuff, and how the default templates are put together.
12 Once we have an understanding of what Maypole does for us automatically,
13 we can begin to customize and create our own templates and actions.
14
15 =head2 The standard actions
16
17 A simple, uncustomized Maypole model class, such as one of the classes
18 in the beer database application, provides the following default actions
19 - that is, provides access to the following URLs:
20
21 =over 3
22
23 =item C</[table]/view/[id]>
24
25 This takes the ID of an object in a table, retrieves the object, and
26 presents it to the F<view> template.
27
28 =item C</[table]/edit/[id]>
29
30 This is the same as C<view>, but uses the F<edit> template to provide a
31 web form to edit the object; it submits to C<do_edit>.
32
33 =item C</[table]/do_edit/[id]>
34
35 =item C</[table]/do_edit/>
36
37 This provides both editing and row creation facilities. 
38
39 =item C</[table]/delete/id>
40
41 This deletes a row, returning to the C<list> page.
42
43 =item C</[table]/list/>
44
45 This provides a paged list of the table suitable for browsing.
46
47 =item C</[table]/search/>
48
49 This handles a search query and presents the search results back to the
50 F<list> template.
51
52 =back
53
54 We'll now look at how these actions are implemented, before moving on to
55 take a detailed look at the templates they drive.
56
57 =head3 C<view> and C<edit>
58
59 These actions are very simple; their job is to take a row ID, turn it
60 into an object, and hand it to the template to be displayed. However, as
61 taking the first argument and turning it into an object is such a common
62 action, it is handled directly by the model class's C<process> method.
63 Similarly, the default template name provided by the C<process> method
64 is the name of the acction, and so will be C<view> or C<edit>
65 accordingly. 
66
67 So the code required to make these two actions work turns out to be:
68
69     sub view :Exported { }
70     sub edit :Exported { }
71
72 That's right - no code at all. This shows the power of the templating
73 side of the system. If you think about it for a moment, it is natural
74 that these actions should not have any code - after all, we have
75 separated out the concerns of "acting" and displaying. Both of these
76 "actions" are purely concerned with displaying a record, and don't need
77 to do any "acting". Remember that the "edit" method doesn't actually do
78 any editing - this is provided by C<do_edit>; it is just another view of
79 the data, albeit once which allows the data to be modified later. These
80 two methods don't need to modify the row in any way, they don't need to
81 do anything clever. They just are.
82
83 So why do we need the subroutines at all? If the subroutines did not exist,
84 we would be sent to the C<view> and C<edit> templates as would be
85 expected, but these templates would not be provided with the right
86 arguments; we need to go through the C<process> method in order to turn
87 the URL argument into a row and thence into an object to be fed to the
88 template. By exporting these methods, even though they contain no code
89 themselves, we force Maypole to call C<process> and provide the class
90 and object to the templates.
91
92 The moral of this story is that if you need to have an action which is
93 purely concerned with display, not acting, but needs to receive an ID
94 and turn it into an object, then create an empty method. For instance,
95 if we want to make an alternate view of a row which only showed the
96 important columns, we might create a method
97
98     sub short_view :Exported {}
99
100 This will cause the row to be turned into an object and fed to the
101 C<short_view> template, and that template would be responsible for
102 selecting the particular columns to be displayed.
103
104 =head3 C<do_edit>
105
106 This action, on the other hand, actually has to do something. If it's
107 provided with an ID, this is turned into an object and we're in edit
108 mode, acting upon that object. If not, we're in create mode. 
109
110     sub do_edit :Exported {
111         my ($self, $r) = @_;
112         my $h = CGI::Untaint->new(%{$r->{params}});
113         my ($obj) = @{$r->objects || []};
114         if ($obj) {
115             # We have something to edit
116             $obj->update_from_cgi($h);
117         } else {
118             $obj = $self->create_from_cgi($h);
119         }
120
121 The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
122 into database table data. This in turn uses C<CGI::Untaint> to ensure
123 that the data coming in is suitable for the table. If you're using the
124 default C<CDBI> model, then, you're going to need to set up your tables
125 in a way that makes C<FromCGI> happy.
126
127 =over 
128
129 =item Digression on C<Class::DBI::FromCGI>
130
131 C<CGI::Untaint> is a mechanism for testing that incoming form data
132 conforms to various properties. For instance, given a C<CGI::Untaint>
133 object that encapsulates some C<POST> parameters, we can extract an
134 integer like so:
135
136     $h->extract(-as_integer => "score");
137
138 This checks that the C<score> parameter is an integer, and returns it if
139 it is; if not, C<< $h->error >> will be set to an appropriate error
140 message. Other tests by which you can extract your data are C<as_hex>
141 and C<as_printable>, which tests for a valid hex number and an ordinary
142 printable string respectively; there are other handlers available on
143 CPAN, and you can make your own, as documented in L<CGI::Untaint>.
144
145 To tell the C<FromCGI> handler what handler to use for each of your
146 columns, you need to use the C<untaint_columns> methods in the classes
147 representing your tables. For instance:
148
149     BeerDB::Beer->untaint_columns(
150         integer => ["score", ... ],
151     );
152
153 This must be done after the call to C<setup> in your handler, because
154 otherwise the model classes won't have been set up to inherit from
155 C<Class::DBI::FromCGI>.
156
157 Remember that if you want to use drop-downs to set the value of related
158 fields, such as the brewery for a beer, you need to untaint these as
159 something acceptable for the primary key of that table:
160
161     BeerDB::Beer->untaint_columns(
162         integer => ["score", "brewery", "style" ],
163         ...
164     );
165
166 This is usually integer, if you're using numeric IDs for your primary
167 key. If not, you probably want C<printable>, but you probably know what
168 you're doing anyway.
169
170 =back
171
172 The data is untainted, and any errors are collected into a hash which is
173 passed to the template. We also pass back in the parameters, so that the
174 template can re-fill the form fields with the original values. The user
175 is then sent back to the C<edit> template.
176
177         if (my %errors = $obj->cgi_update_errors) {
178             # Set it up as it was:
179             warn "There were errors: ".Dumper(\%errors)."\n";
180             $r->{template_args}{cgi_params} = $r->{params};
181             $r->{template_args}{errors} = \%errors;
182             $r->{template} = "edit";
183         }
184
185 Otherwise, the user is taken back to viewing the new object:
186
187     } else {
188         $r->{template} = "view";
189     }
190     $r->objects([ $obj ]);
191
192 Notice that this does use hard-coded names for the templates to go to next.
193 Feel free to override this in your subclasses:
194
195     sub do_edit :Exported {
196         my ($class, $r) = @_;
197         $class->SUPER::do_edit($r);
198         $r->template("my_edit");
199     }
200
201 =head3 delete
202
203 The delete method takes a number of arguments and deletes those rows from the
204 database; it then loads up all rows and heads to the F<list> template.
205 You almost certainly want to override this to provide some kind of
206 authentication.
207
208 =head3 list
209
210 Listing, like viewing, is a matter of selecting objects for
211 presentation. This time, instead of a single object specified in the
212 URL, we want, by default, all the records in the table:
213
214     sub list :Exported {
215         my ($class, $r) = @_;
216         $r->objects([ $self->retrieve_all ])
217     }
218
219 However, things are slightly complicated by paging and ordering by
220 column; the default implementation also provides a C<Class::DBI::Pager>
221 object to the templates and uses that to retrieve the appropriate bit of
222 the data, as specified by the C<page> URL query parameter. See the F<pager> 
223 template below.
224
225 =head3 search
226
227 Searching also uses paging, and creates a query from the C<POST>
228 parameters. It uses the F<list> template to display the objects once
229 they've been selected from the database.
230
231 =head2 The templates and macros
232
233 Once these actions have done their work, they hand a set of objects to
234 the templates; if you haven't specified your own custom template
235 globally or for a given class, you'll be using the factory specified
236 template. Let's take a look now at each of these and how they're put
237 together.
238
239 The beauty of the factory specified templates is that they make use of
240 the classes' metadata as supplied by the view class. Although you're
241 strongly encouraged to write your own templates, in which you don't need
242 to necessarily be as generic, the factory templates will always do the
243 right thing for any class without further modification.
244
245 =head3 F<view> 
246 =head3 F<edit>
247 =head3 F<list>