\chapter{CRUD in Gantry}
\label{chap:fullcrud}

Gantry provides two schemes for automating database Create, Update, and
Delete (CRUD).  (Note that retrieval is handled in other ways, usually with
direct help from an Object-Relational Mapper, a.k.a. an ORM).  One scheme does
everything for you.  It is called AutoCRUD, since it can do everything
-- except make your form.  But, it does have various controls and hooks for
customization.  The other scheme does form validation and redirections, with
a lot of code from you.  It is called CRUD, to indicate the extra work
you must do to use it.  This chapter describes both schemes.

Since one of the schemes is called CRUD, when I want to refer to both of
them I will use the lowercase crud -- even though this is acronym abuse.

The two schemes do have one main feature in common, they both display a
form in which the user adds to or modifies values in the database.  The
first section, immediately below, describes these forms.  The later
sections describe each scheme's unique features.

\section{Forms}

Both crud schemes use Data::FormValidator for form validation and share
form.tt as their default form template.  Thus, their form methods are very
similar.  While bigtop can make the form method for you, you might still
want to know what is in it or how to roll your own, hence this section.

Form routines are called during add and edit operations by the crud scheme.
They are called in slightly (but only slightly) different ways by CRUD and
AutoCRUD.  Here is the difference: during edit AutoCRUD passes the ORM row
object being edited to the form method, while CRUD always passes whatever
data you passed to its add or edit method.  You should put the row object
in there, but you can add anything else your method needs.  Other than that,
the methods are the same.

I will now begin assuming that you are using the Gantry standard form.tt
to display the form.  If you have made your own replacement, you must
return the structure it expects from your form method.  Even so, the fields
array must be the same, otherwise form validation won't work.

Your form method must return a hash reference, which will be passed
directly to form.tt.  Further, its 'fields' key will be passed to the
interanl \verb+form_profile+ method.  It places certain expectations on
the members of the fields array, which are needed to use Data::FormValidator,
see below.

Here is a table showing the top level keys in the hash you should return
(required keys are listed at the top for ease of reference):

\begin{tabular}{l|l|l}
Key                & Required?    & Value \\
\hline
\verb+fields+      & Required     & An array of fields which appear on
                                    the form.  See below.               \\
\verb+row+         & Required
                     when editing & The ORM object being edited.        \\
\hline
\verb+action+      & Optional     & The URL which will process the page,
                                    defaults to \verb+$self->uri$+.     \\
\verb+cellspacing+ & Optional     & The cellspacing of the form's table,
                                    defaults to 0.                      \\
\verb+change_log+  & Optional     & An array of change log entries. 
                                    See below.                          \\
\verb+javascript+  & Optional     & Any Javascript your form needs.     \\
\verb+legend+      & Optional     & The fieldset legend above the form. \\
\verb+method+      & Optional     & POST or GET, defaults to POST.      \\
\verb+name+        & Optional     & The HTML DOM name of the form.      \\
\verb+width+       & Optional     & The width of the form's table.
                                    By default with width attribute
                                    is omitted.                         \\
\end{tabular}

Most of these are optional and are not particularly interesting.  A couple
need a few more words.  These are: \verb+fields+, \verb+change_log+,
and \verb+javascript+.  I'll take them in reverse order.

Some forms need to add javascript.  If yours does, use the \verb+javascript+
key.  Give it a value that is a correct and balanced script tag.  One
particularly useful way to get one of those is by calling a method that
makes them.  For instance, if you want date popups, you can use
Gantry::Plugins::Calendar, then call \verb+calendar_month_js+ on yourself
with the name of your form, something like this:

\begin{verbatim}
    use Gantry::Plugins::Calendar;

    #...
    sub form {
        my ( $self, $row ) = @_;

        #...
        return {
            name => 'my_form',
            #...
            javascript => $self->calendar_month_js( 'my_form' ),
            #...
        }
    }
\end{verbatim}

Then, if any of your fields have \verb+date_select_text+ (see below), users
will be given a link to a popup calendar to populate their date fields for
them.

The \verb+change_log+ key is quite specialized, so you may never need it.
But, if you want to show a little change log summary on the right side of
your add/edit form, include this key and fill its value with a reference
to an array of hashes, each of which has these keys:

\begin{tabular}{l|l}
Key                & Value \\
\hline
date               & The date the change happened.               \\
by                 & The name of the person who made the change. \\
message            & What happened.                              \\
\end{tabular}

Note that you must format the dates yourself, form.tt will not do that.

Finally, we come to the main point of interest: the \verb+fields+ key.
The value for this key is an array reference.  Each element of the array
is a hash reference.  There are two types of keys in each hash: those
that depend on the type of the field and those that don't.  Here are the
type independent keys:

\begin{tabular}{l|l}
Key & Value \\
\hline
\verb+default_value+ & The fields value if no other value can be found.   \\
\verb+label+         & What the user sees to the left of the input field. \\
\verb+name+          & The name of the database column for this field,    \\
                     & which is also the HTML name of its input element.  \\
\verb+raw_html+      & Copied directly to HTML output immediately before  \\
                     & the field's table row.                             \\
\verb+type+          & One of: text, textarea, or select.                 \\
\verb+width+         & Width of the \verb+<+td\verb+>+ surrounding the
                       input element.   \\
\end{tabular}

A few words about defaults is in order.  The default value for a field
is chosen through a series of tests.  First, when the user clicks `Save,'
but Data::FormValidator does not like one or more of the fields, when the
page refreshes, default values will be whatever the user entered.  Second,
if the form has not yet been submitted, and a row is being edited, the
values in the database for that row are the defaults.  Third, if nothing
else has set a default, the \verb+default_value+ in the fields array is
used.  Finally, when all else fails the normal HTML form defaults apply.

The remaining keys depend on the type key, so I'll show you one table
for each type.

Fields of type select have this additional required key:

\begin{tabular}{l|l}
Key & Value\\
\hline
\verb+options+ & An array of hashes with value and label keys.\\
\end{tabular}

You can often avoid coding the options for your select field by calling
\verb+get_form_selections+ on your model.  That method returns a hash
you can use like this:

\begin{verbatim}
    my $selections = $MY_MODEL->get_form_selections();

    #...
    return {
        #...
        fields => [
            {
                name    => other_table_id,
                label   => 'Other Table',
                type    => 'select',
                options => $selections->{other_table},
            },
            #...
        ],
    }
\end{verbatim}

Note that you need to pass your schema to \verb+get_form_selections+,
if you are using DBIx::Class.  In that case use Gantry::Plugins::DBIxClassConn,
in your controller and call \verb+get_schema+ on your self object to get
the schema.

Fields of type textarea have these additional optional keys:

\begin{tabular}{l|l}
Key & Value\\
\hline
\verb+rows+ & The HTML rows attribute for the textarea element. \\
\verb+cols+ & The HTML cols attribute for the textarea element. \\
\end{tabular}

Fields of type text have these additional optional keys:

\begin{tabular}{l|l}
Key & Value\\
\hline
\verb+date_select_text+ & Indicates that the field is a date the user may\\
                        & populate with a popup calendar windo. The value \\
                        & is the text of the link which pops up the calendar.\\
\verb+display_size+ & The HTML size attribute for the text input element. \\
\end{tabular}

The size is called \verb+display_size+ to avoid a collision with a
Template Toolkit pseudo-method called size.

You may include other keys in the fields hashes as well.  For instance,
bigtop, pollutes the hash with extra information meant for use during
project generation.  But it could help you too, since the extra keys
are available to any caller of the form method.  One of those caller's
might be you.

\section{AutoCRUD}

In most cases, AutoCRUD is all you need.  Yet, some prefer to take more
control even when they don't actually need to.  Feel free to use CRUD even
when AutoCRUD would do, the next section shows you how.  But, if you're as
lazy as I am, automation sounds good and this section is for you.

When bigtop makes your controllers for use with AutoCRUD, it assumes you
don't want to do any work.  This may give you the impression that you can't
control anything it does, let's disabuse you of that notion.

Before moving on to the really interesting parts, here are the things
that are easily controlled by implementing simple methods.

One of the easiest things to control is the template used to show the form.
By default that is form.tt which ships with Gantry.  To change it,
put a form template into your Template Toolkit root path and implement
a method in your controller:

\begin{verbatim}
sub form_name { return 'my_form_name.tt'; }
\end{verbatim}

When you implement \verb+form_name+, remember to omit that method from
your use statement for Gantry::Plugins::AutoCRUD.  Otherwise, you'll get
a redefinition warning, creating confusion over which method is in effect.

Another easily controlled item is the text description users see in browser
title bars and in the blank in `Delete \verb+____+?'  You must specify this
with a method like this:

\begin{verbatim}
sub text_descr { return 'item'; }
\end{verbatim}

The other controllable parts require a bit more work, so they have their
own subsections.

\subsection*{Controlling Redirection}

At the bottom of any form.tt generated form is a pair of buttons: Save
and Cancel.  Cancel is always successful, pressing it takes the user
somewhere.  By default they return to the page from which they clicked
`Add,' `Edit,' or `Delete.'  Also by default, when Saving is successful,
they are delivered to the same place.  But you can override the
defaults, to gain complete programmatic control of the redirection
location, which I will now begin abbreviating as the relocation.

There are two approaches to controlling relocations.  First, you can implement
a single method called \verb+get_relocation+.  It receives two parameters.
First is the action underway: add, edit, or delete.  Second is the
button pressed: submit or cancel.  From these, you decide where the
user should go and return any valid URL.  For example:

\begin{verbatim}
sub get_relocation {
    my ( $self, $update_type, $button_pressed ) = @_;

    return '/site_home';
}
\end{verbatim}

Obviously, you'll want to do something more interesting than always sending
everyone to the same top level location.  As you work, keep in mind that
the Gantry site object (called \verb+$self+ above) is full of useful
information you can use to decide where the user should go.  For example, it
has \verb+location+ and \verb+uri+ methods and many others.  See the
docs for Gantry.pm for a list of available methods, but note that various
plugins add to that list.

Implementing a single relocation method can become tedious when you need
to do a lot of testing to determine where the user should go.  So,
as an alternative, you may choose to implement two methods instead: one
for cancelations and the other for successful submissions.

For consistency, these methods are called with exactly the same parameters
as \verb+get_relocation+, even though the pressed button is redudant for them.
Here are examples:

\begin{verbatim}
sub get_cancel_loc {
    my ( $self, $update_type, $button_pressed ) = @_;

    return '/home/sorry';
}
sub get_submit_loc {
    my ( $self, $update_type, $button_pressed ) = @_;

    return '/home/thanks';
}
\end{verbatim}

Note that if you have a \verb+get_relocation+, it will be used and both
\verb+get_cancel_loc+ and \verb+get_save_loc+ will be ignored.

\subsection*{Data Cleanup Hooks and Hard Bail Outs}

After the user submits the form and it has been validated, but before
making any changes to the database AutoCRUD will call hook methods, if
they are defined.  These are the prehooks, since they happen prior to
database changes.

For example, just before a successful row addition, AutoCRUD attempts
to call \verb+add_pre_action+.  If the method exists, it will be called
with a reference to the hash of form values (minus the submit key).
Perform any needed operations on that hash and return nothing.  This is
a good place to convert numbers to booleans, to add creation times,
etc.

Similarly, \verb+edit_pre_action+ is called immediately before a row
is updated due to an edit and receives the ORM row object to be changed
and a reference to the hash of form values (again with submit already
deleted).  You should make changes to the form hash, not to the object,
since the hash will be used to update the object.  This is good place
to specify modified times, etc.

Finally, \verb+delete_pre_aciton+ is called after the user has confirmed
that a row should be deleted from the table.  This method, is called with
the doomed row object courtesy of the ORM.

If your pre hook does not like the change about to be made, it has no
choice but to use croak (or die).  That is the only way to stop the madness.
If you need a better bail out mechanism, you need to use real CRUD -- see
the next section.

Note that if all you need to do in your pre hooks is set false and integers
dto NULL, you don't need to bother, since AutoCRUD knows to do that.  All
you need to do is make sure the form fields have an \verb+is+ key and that
it is only boolean for actual booleans.  If you don't want that behavior
or have more date work to do, either implement a pre hook or use CRUD.

\subsection*{Post Update Hooks}

Immediately after committing changes to the database, AutoCRUD attempts
to call a post hook.  These are named \verb+add_post_action+,
\verb+edit_post_action+, and \verb+delete_post_action+.  

The add and edit post hooks are called with the newly created or freshly
modified row object.  Since the row is gone when it gets called, the delete
post hook is given the id of the deceased row.  These are good places to log
changes.

\subsection*{Changing ORM Schemes}

By default, for historical reasons, AutoCRUD expects your ORM to
share the API of Class::DBI.  If your ORM has a different API, you
need to provide a bit of help to AutoCRUD.

First, your base module should include a method like this:

\begin{verbatim}
sub get_orm_helper {
    return 'Gantry::Plugins::AutoCRUDHelper::SomeModule';
}
\end{verbatim}

Then you need the ORM helper module.  It is not necessary for it live in the
\verb+Gantry::Plugins::AutoCRUDHelper+ namespace, but it can't hurt.
For example, if you use DBIx::Class -- as we now do -- your
\verb+get_orm_helper+ should return
\verb+Gantry::Plugins::AutoCRUDHelper::DBIxClass+.  If there is not already
a helper for your ORM, don't worry.  They are easy to write.

Each AutoCRUDHelper must implement for class methods: insert, retrieve,
update, and delete.  Remember that these are class methods, so their
first argument is always the invoking class.

All of the methods below are passed the Gantry site object.  You probably
need to call \verb+get_model_name+ to find out your table name.  Depending
on how your models work, you may need to call another method on the result
(e.g. when using DBIx::Class you must call \verb+table_name+).

The insert method receives the Gantry site object and a hash reference of
form parameters.  That hash is already sanatized and ready for immediate
insertion into the database.

The retrieve method receives the Gantry site object and the single column
primary key to retrieve.  

The update method receives the Gantry site object, the ORM row to be
changed (the row was generated by the retrieve method), and a hash reference
of form parameters sanitized and ready for immediate application to the
row.

Finally, the delete method is called with the Gantry site object and the
doomed row object (also obtained from the retrieve method).

Remember to trigger a database commit at the end of each of these helper
methods.  AutoCRUD is fully automatic and it does not know how to commit
with your ORM.

There are currently two AutoCRUDHelpers, one for DBIx::Class and one for
Class::DBI.  Consult them for aditional advice on implementing your own.

\section{CRUD}

The impetus for the creation of CRUD was AutoCRUD's inability to work on two
tables in the same controller.  But, once it came to be, we discovered that
the extra flexibility and transparency are quite nice, and we frequently use
it when AutoCRUD would also work.

For each possible CRUD cycle, you need to create a CRUD object.  During
construction, you fully describe how it should work.  This section describes
all the things you can put in that description.  It explains how
to drive the CRUD in your code in complete detail.  For a single simple
example of CRUD use see the Job controller in Chapter \ref{chap:jobads},
which uses it to link skills to job descriptions.

\subsection*{Summary of CRUD Construction}

Here are all the things you can pass as named paramters to the CRUD
constructor.

\begin{tabular}{l|l}
Parameter Name & Meaning \\
\hline
\verb+add_action+      & Callback used when add data validates.              \\
\verb+edit_action+     & Callback used when edit data validates.             \\
\verb+delete_action+   & Callback used when user confirms a deletion.        \\
\verb+form+            & Callback which must return a form hash.             \\
\verb+redirect+        & Callback used after successful submit or cancel.    \\
\verb+template+        & A string holding your form's template file name.    \\
\verb+text_descr+      & Fills in the blank after `Delete ...?'              \\
\verb+turn_off_clean_params+
                       & Sets all non-boolean false fields to undef before   \\
                       & invoking \verb+add_action+ or \verb+edit_action+.   \\
\verb+use_clean_dates+ & Sets all false date fields to undef before invoking \\
                       & \verb+add_action+ or \verb+edit_action+.            \\
\end{tabular}

If you don't \verb+turn_off_clean_params+, you cannot
\verb+use_clean_dates+, as that would be useless redundency.

So, for example, your constructor call might look like this:

\begin{verbatim}
my $special_crud = Gantry::Plugins::CRUD->new(
    add_action  => \&special_add,
    edit_action => \&special_edit,
    form        => \&special_form,
    text_descr  => 'item',
);
\end{verbatim}

Note that you must pass a list of pairs and not an actual hash reference.

\verb+template+ and \verb+text_description+ are just strings,
while \verb+turn_off_clean_params+ and \verb+use_clean_dates+ are booleans.
The others are all callbacks.  The details of how, when, and with what those
callbacks are called follow below.

If you don't \verb+turn_off_clean_params+, it will walk through all
the fields in the param hash setting the false values to undef, unless
they are booleans.  If you do turn it off, you can turn on
\verb+use_clean_dates+, which does the same thing, but only for date fields.

Keep in mind that there is no rule that all CRUD objects must handle all
three CRUD activities.  If you want one that only adds or only edits, simply
omit the other actions from your constructor call and refrain from calling
them on the resulting object.  Note that the example above has no delete
action, perhaps in the case of this special activity, deletes are not allowed.

If you need different forms for adding and editing, just use two CRUD objects.

\subsection*{The Basics}

Let's start with the most common callbacks, leaving redirects for the next
subsection and remembering that forms were covered in the first section of
this chapter.

The processes don't begin with the callbacks, so we need to step back to the
user actions.  There are typically three of these: \verb+do_add+,
\verb+do_edit+, or \verb+do_delete+, but the names are up to you.  Further,
remember that there are frequently multiple sets of these in modules that
use CRUD.  So you might \verb+do_post_add+ and \verb+do_comment_add+, etc.
In all cases, you write those routines.  In them, you call \verb+add+,
\verb+edit+, or \verb+delete+ on the CRUD object, when it accepts a form
submission, it calls you back.  Here's an example:

\begin{verbatim}
sub do_add {
    my ( $self, $parent_row_id ) = @_;

    $my_crud->add( $self, { parent_row => $parent_row_id } );
}
\end{verbatim}

The other actions (i.e. edit and delete) work similarly, just change the
method names.  Remember to put anything your form will need into the data
hash you pass to the CRUD add or edit methods.

The data hash is also passed to the redirect callback.  I put the parent
row into the hash in the above example, so the redirect callback can go
back to the proper page when the user cancels the operation.  It will also
be there for redirection, when the user successfully submits the form.

When implementing an edit handler, you need to put the row to be edited into
the data hash, so the form will know which row to use for initial default
values.

So, the user's click actually triggers your \verb+do_*+ method.  In it
do any prep work, then call the corresponding method on your CRUD object.
When the user successfully submits the form, CRUD will call you back.
For the above that callback might look like this:

\begin{verbatim}
sub add_action {
    my ( $self, $params, $data ) = @_;

    my $schema = $self->get_schema();
    $schema->resultset( 'mymodel' )->create( $params );

    # ensure proper relocation
    if ( $params->{ parent_row } != $data->{ parent_row } ) {
        $data->{ parent_row } = $params->{ parent_row };
    }
}
\end{verbatim}

Here I've shown how to make a new row using a DBIx::Class schema.  Note
that the add action updates the parent row, which assumes the user had
a chance to change it on the form.  Setting it only in the action would
not work, because cancelations don't flow through the action.

There are many other things you could do in an action, including updating
several table based on the form input or sending mail to interested parties.
But you could also use CRUD as a more transparent alternative to AutoCRUD.

The other sequences are similar:

\begin{verbatim}
sub do_edit {
    my ( $self, $row_id ) = @_;

    my $schema = $self->get_schema();
    my $row    = $schema->resultset( 'mymodel' )->find( $row_id );

    $my_crud->edit( $self, { row => $row } );
}

sub edit_action {
    my ( $self, $params, $data ) = @_;

    $params->{ modified } = 'now';

    $data->{ row }->update( $params );
}

sub do_delete {
    my ( $self, $row_id, $yes ) = @_;

    my $schema = $self->get_schema();
    my $row    = $schema->resultset( 'mymodel' )->find( $row_id );

    $my_crud->delete( $self, $yes, { row => $row } );
}

sub delete_action {
    my ( $self, $data ) = @_;

    $data->{ row }->delete;
}
\end{verbatim}

Note that the delete method receives the doomed row id and the user's
confimation flag, both of those must be forwarded on to the CRUD object's
delete method.

\subsection*{Controlling Redirection}

After the user either cancels or successfully submits a form, you want
to take them to an appropriate place.  By default that place will come from
calling \verb+location+ on the Gantry site object.  If you want users to
go somewhere else, you need to supply a code reference as the \verb+redirect+
parameter to your CRUD constructor.

When your redirect callback is invoked, CRUD will pass it four things:
(1) your Gantry site object, (2) your data hash, (both of these you must
pass when calling \verb+add+, \verb+edit+, or \verb+delete+ on your CRUD
object), (3) the button action: `cancel' or `submit', and (4) the CRUD
method: `add', `edit', or `delete'.  From these you decide where to go.
Once you decide, return a value URL in a string:

\begin{verbatim}
sub redirect_to_main {
    my ( $self, $data, $button_action, $request ) = @_;

    if ( $data{ parent_id } ) {
        return $self->app_rootp . '/base/main/' . $data->{ parent_id };
    }
    elsif ( $data{ row } ) {
        return $self->app_rootp . '/base/main/' . $data->{ row }->parent_id;
    }
    else {
        return $self->location;
    }
}
\end{verbatim}

Note that if you return an undefined value, the default relocation will
be used.  That is if the else block above had a bare return, the user would
go to \verb+$self->location+ anyway.

That's about all there is to say about CRUD and AutoCRUD.  The only things
not covered here are the specifics of Data::FormValidator which has its
own fine documentation.
