]> git.decadent.org.uk Git - maypole.git/blobdiff - lib/Maypole/Manual/StandardTemplates.pod
Moved doc/*.pod to lib/Maypole/Manual/ and added new Maypole.pm
[maypole.git] / lib / Maypole / Manual / StandardTemplates.pod
diff --git a/lib/Maypole/Manual/StandardTemplates.pod b/lib/Maypole/Manual/StandardTemplates.pod
new file mode 100644 (file)
index 0000000..e5eb9f0
--- /dev/null
@@ -0,0 +1,288 @@
+
+=head1 Maypole's Standard Templates and Actions
+
+As we saw in our CRUD example, Maypole does all it can to make your life
+easier; this inclues providing a set of default actions and
+factory-supplied templates. These are written in such a generic way,
+making extensive use of class metadata, that they are more or less
+applicable to any table or application. However, in order to progress
+from automatically generated CRUD applications to real customized
+applications, we need to begin by understanding how these default
+actions do their stuff, and how the default templates are put together.
+Once we have an understanding of what Maypole does for us automatically,
+we can begin to customize and create our own templates and actions.
+
+=head2 The standard actions
+
+A simple, uncustomized Maypole model class, such as one of the classes
+in the beer database application, provides the following default actions
+- that is, provides access to the following URLs:
+
+=over 3
+
+=item C</[table]/view/[id]>
+
+This takes the ID of an object in a table, retrieves the object, and
+presents it to the F<view> template.
+
+=item C</[table]/edit/[id]>
+
+This is the same as C<view>, but uses the F<edit> template to provide a
+web form to edit the object; it submits to C<do_edit>.
+
+=item C</[table]/do_edit/[id]>
+
+=item C</[table]/do_edit/>
+
+This provides both editing and row creation facilities. 
+
+=item C</[table]/delete/id>
+
+This deletes a row, returning to the C<list> page.
+
+=item C</[table]/list/>
+
+This provides a paged list of the table suitable for browsing.
+
+=item C</[table]/search/>
+
+This handles a search query and presents the search results back to the
+F<list> template.
+
+=back
+
+We'll now look at how these actions are implemented, before moving on to
+take a detailed look at the templates they drive.
+
+=head3 C<view> and C<edit>
+
+These actions are very simple; their job is to take a row ID, turn it
+into an object, and hand it to the template to be displayed. However, as
+taking the first argument and turning it into an object is such a common
+action, it is handled directly by the model class's C<process> method.
+Similarly, the default template name provided by the C<process> method
+is the name of the acction, and so will be C<view> or C<edit>
+accordingly. 
+
+So the code required to make these two actions work turns out to be:
+
+    sub view :Exported { }
+    sub edit :Exported { }
+
+That's right - no code at all. This shows the power of the templating
+side of the system. If you think about it for a moment, it is natural
+that these actions should not have any code - after all, we have
+separated out the concerns of "acting" and displaying. Both of these
+"actions" are purely concerned with displaying a record, and don't need
+to do any "acting". Remember that the "edit" method doesn't actually do
+any editing - this is provided by C<do_edit>; it is just another view of
+the data, albeit once which allows the data to be modified later. These
+two methods don't need to modify the row in any way, they don't need to
+do anything clever. They just are.
+
+So why do we need the subroutines at all? If the subroutines did not exist,
+we would be sent to the C<view> and C<edit> templates as would be
+expected, but these templates would not be provided with the right
+arguments; we need to go through the C<process> method in order to turn
+the URL argument into a row and thence into an object to be fed to the
+template. By exporting these methods, even though they contain no code
+themselves, we force Maypole to call C<process> and provide the class
+and object to the templates.
+
+The moral of this story is that if you need to have an action which is
+purely concerned with display, not acting, but needs to receive an ID
+and turn it into an object, then create an empty method. For instance,
+if we want to make an alternate view of a row which only showed the
+important columns, we might create a method
+
+    sub short_view :Exported {}
+
+This will cause the row to be turned into an object and fed to the
+C<short_view> template, and that template would be responsible for
+selecting the particular columns to be displayed.
+
+=head3 C<do_edit>
+
+This action, on the other hand, actually has to do something. If it's
+provided with an ID, this is turned into an object and we're in edit
+mode, acting upon that object. If not, we're in create mode. 
+
+    sub do_edit :Exported {
+        my ($self, $r) = @_;
+        my $h = CGI::Untaint->new(%{$r->{params}});
+        my ($obj) = @{$r->objects || []};
+        if ($obj) {
+            # We have something to edit
+            $obj->update_from_cgi($h);
+        } else {
+            $obj = $self->create_from_cgi($h);
+        }
+
+The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
+into database table data. This in turn uses C<CGI::Untaint> to ensure
+that the data coming in is suitable for the table. If you're using the
+default C<CDBI> model, then, you're going to need to set up your tables
+in a way that makes C<FromCGI> happy.
+
+=over 
+
+=item Digression on C<Class::DBI::FromCGI>
+
+C<CGI::Untaint> is a mechanism for testing that incoming form data
+conforms to various properties. For instance, given a C<CGI::Untaint>
+object that encapsulates some C<POST> parameters, we can extract an
+integer like so:
+
+    $h->extract(-as_integer => "score");
+
+This checks that the C<score> parameter is an integer, and returns it if
+it is; if not, C<< $h->error >> will be set to an appropriate error
+message. Other tests by which you can extract your data are C<as_hex>
+and C<as_printable>, which tests for a valid hex number and an ordinary
+printable string respectively; there are other handlers available on
+CPAN, and you can make your own, as documented in L<CGI::Untaint>.
+
+To tell the C<FromCGI> handler what handler to use for each of your
+columns, you need to use the C<untaint_columns> methods in the classes
+representing your tables. For instance:
+
+    BeerDB::Beer->untaint_columns(
+        integer => ["score", ... ],
+    );
+
+This must be done after the call to C<setup> in your handler, because
+otherwise the model classes won't have been set up to inherit from
+C<Class::DBI::FromCGI>.
+
+Remember that if you want to use drop-downs to set the value of related
+fields, such as the brewery for a beer, you need to untaint these as
+something acceptable for the primary key of that table:
+
+    BeerDB::Beer->untaint_columns(
+        integer => ["score", "brewery", "style" ],
+        ...
+    );
+
+This is usually integer, if you're using numeric IDs for your primary
+key. If not, you probably want C<printable>, but you probably know what
+you're doing anyway.
+
+=back
+
+The data is untainted, and any errors are collected into a hash which is
+passed to the template. We also pass back in the parameters, so that the
+template can re-fill the form fields with the original values. The user
+is then sent back to the C<edit> template.
+
+        if (my %errors = $obj->cgi_update_errors) {
+            # Set it up as it was:
+            $r->{template_args}{cgi_params} = $r->{params};
+            $r->{template_args}{errors} = \%errors;
+            $r->{template} = "edit";
+        }
+
+Otherwise, the user is taken back to viewing the new object:
+
+    } else {
+        $r->{template} = "view";
+    }
+    $r->objects([ $obj ]);
+
+Notice that this does use hard-coded names for the templates to go to next.
+Feel free to override this in your subclasses:
+
+    sub do_edit :Exported {
+        my ($class, $r) = @_;
+        $class->SUPER::do_edit($r);
+        $r->template("my_edit");
+    }
+
+=head3 delete
+
+The delete method takes a number of arguments and deletes those rows from the
+database; it then loads up all rows and heads to the F<list> template.
+You almost certainly want to override this to provide some kind of
+authentication.
+
+=head3 list
+
+Listing, like viewing, is a matter of selecting objects for
+presentation. This time, instead of a single object specified in the
+URL, we want, by default, all the records in the table:
+
+    sub list :Exported {
+        my ($class, $r) = @_;
+        $r->objects([ $self->retrieve_all ])
+    }
+
+However, things are slightly complicated by paging and ordering by
+column; the default implementation also provides a C<Class::DBI::Pager>
+object to the templates and uses that to retrieve the appropriate bit of
+the data, as specified by the C<page> URL query parameter. See the F<pager> 
+template below.
+
+=head3 search
+
+Searching also uses paging, and creates a query from the C<POST>
+parameters. It uses the F<list> template to display the objects once
+they've been selected from the database.
+
+=head2 The templates and macros
+
+Once these actions have done their work, they hand a set of objects to
+the templates; if you haven't specified your own custom template
+globally or for a given class, you'll be using the factory specified
+template. Let's take a look now at each of these and how they're put
+together.
+
+The beauty of the factory specified templates is that they make use of
+the classes' metadata as supplied by the view class. Although you're
+strongly encouraged to write your own templates, in which you don't need
+to necessarily be as generic, the factory templates will always do the
+right thing for any class without further modification, and as such are
+useful examples of how to build Maypole templates.
+
+=head3 Commonalities
+
+There are certain common elements to a template, and these are extracted
+out. For instance, all the templates call the F<header> template to
+output a HTML header, and nearly all include the F<macros> template to
+load up some common template functions. We'll look at these common
+macros as we come across them.
+
+=head3 F<view> 
+
+=template view
+
+=head3 F<edit>
+
+The F<edit> template is pretty much the same as F<view>, but it uses the
+C<to_field> method on each column of an object to return a C<HTML::Element>
+object representing a form element to edit that property. These elements
+are then rendered to HTML with C<as_HTML>. It expects to see a list of
+editing errors, if any, in the C<errors> template variable:
+
+     FOR col = classmetadata.columns;
+        NEXT IF col == "id";
+        "<P>";
+        "<B>"; classmetadata.colnames.$col; "</B>";
+        ": ";
+            item.to_field(col).as_HTML;
+        "</P>";
+        IF errors.$col;
+            "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
+        END;
+    END;
+
+=head3 F<list>
+
+Browsing records and search results are both handled by the F<list> template.
+The C<search> template argument is used to distinguish between the two cases:
+
+    [% IF search %]
+    <h2> Search results </h2>
+    [% ELSE %]
+    <h2> Listing of all [% classmetadata.plural %]</h2>
+    [% END %]
+
+=head1 Customizing Generic CRUD Applications