Gantry

 view release on metacpan or  search on metacpan

lib/Gantry/Docs/Tutorial.pod  view on Meta::CPAN

a table row in the template data.

        foreach my $row ( @rows ) {
            my $id = $row->id;
            push(
                @{ $retval->{rows} }, {
                    data => [
                        $row->name,
                        $row->street,
                    ],
                    options => [
                        {
                            text => 'Edit',
                            link => $real_location . "edit/$id",
                        },
                        {
                            text => 'Delete',
                            link => $real_location . "delete/$id",
                        },
                    ],
                }
            );
        }

First, I fished the row id out of the DBIx::Class object, and stored it
in a scalar.  This allows direct interpolation into a couple of URL link
strings.  Then I pushed the data and the row options into the C<rows>
key of the return value hash.  The data are just the family's name and
street address.  You could add any other columns from the underlying
table.  For instance, to add phone, add:

    $row->phone,

to the data list after C<$row->street>.  The order and contents of the
data are up to you.

Just as the header row has an Add option, each data row has an Edit
and a Delete option.  Note that their URLs include the row id to work on.
The only thing I have left to do in C<do_main> is to set the return value data
in place:

        $self->stash->view->data( $retval );
    } # END do_main

The only other large piece is the form, in which users enter new addresses
or edit existing ones.  AutoCRUD calls this method for you when the users
visits do_add and do_edit pages.  Call this method form.  If an edit triggered
the call, it will pass in the row as it stands in the database.

The following code produces this on the screen:

=for html <img src='http://www.usegantry.org/images/form.png' alt='Form Screen Shot' />

    #-----------------------------------------------------------------
    # $self->form( $row )
    #-----------------------------------------------------------------
    sub form {
 
        return {
            row    => $row,
            legend => $self->path_info =~ /edit/i ? 'Edit' : 'Add',
            fields => [

The default template is called C<form.tt>.  Among other things, it expects
the return value hash to contain C<row> (if editing), C<legend> (legend
of form's fieldset), and C<fields> (what the user will see and enter or edit).
If the C<row> is supplied, its values are used for initial form population.
The C<legend> is set based on the C<path_info> which contains part of the
URL.  If that URL fragment includes 'edit,' the legend is 'Edit.'  Otherwise,
it is 'Add.'

The C<fields> are an array of the entry elements the user will see.  The
order of the array controls the on screen order.  Each field is a little
hash.  While there are other keys, the four most common are used over and
over, not just in this example.

                {
                    name => 'name',
                    optional => 0,
                    label => 'Name',
                    type => 'text',
                },

The C<name> must be the name of the column in the database and will also
be used as the name of the html form element.

If C<optional> is true, the field is optional.  Otherwise, it is required.
I could have omitted optional from the Name hash, since required is the
default.

The C<label> is displayed in the left hand column of the form input table.

The C<type> is the HTML form element type.  See C<form.tt> in Gantry's
templates for a complete list of types is understands.  That will also
explain how to include other field hash keys to specify things like
pull down options.

The other C<fields> hashes are all of the same form.  Only the field names
and labels change.  Here is one example:

                {
                    name => 'city',
                    optional => 1,
                    label => 'City',
                    type => 'text',
                },
                #...
            ],
        };
    } # END form

Finally, there are some small subs which return strings used by the
AutoCRUD plugin at various points.

    #-----------------------------------------------------------------
    # get_model_name( )
    #-----------------------------------------------------------------
    sub get_model_name {
        return $ADDRESS;
    }

Gantry::Plugins::AutoCRUD uses get_model_name to find out which model
class to use for create, update, delete, and lookups.

    #-----------------------------------------------------------------
    # get_orm_helper( )
    #-----------------------------------------------------------------
    sub get_orm_helper {
        return 'Gantry::Plugins::AutoCRUDHelper::DBIxClass';

lib/Gantry/Docs/Tutorial.pod  view on Meta::CPAN

        my ( $self ) = @_;
 
        $self->stash->view->template( 'results.tt' );
        $self->stash->view->title( 'Address' );
 
        my $real_location = $self->location() || '';
        if ( $real_location ) {
            $real_location =~ s{/+$}{};
            $real_location .= '/';
        }
 
        my $retval = {
            headings       => [
                'Name',
                'Street',
            ],
            header_options => [
                {
                    text => 'Add',
                    link => $real_location . "add",
                },
            ],
        };
 
        my $schema = $self->get_schema();
        my @rows   = $ADDRESS->get_listing( { schema => $schema } );
 
        foreach my $row ( @rows ) {
            my $id = $row->id;
            push(
                @{ $retval->{rows} }, {
                    data => [
                        $row->name,
                        $row->street,
                    ],
                    options => [
                        {
                            text => 'Edit',
                            link => $real_location . "edit/$id",
                        },
                        {
                            text => 'Delete',
                            link => $real_location . "delete/$id",
                        },
                    ],
                }
            );
        }
 
        $self->stash->view->data( $retval );
    } # END do_main
 
    #-----------------------------------------------------------------
    # $self->form( $row )
    #-----------------------------------------------------------------
    sub form {
        my ( $self, $row ) = @_;
 
        return {
            row        => $row,
            legend => $self->path_info =~ /edit/i ? 'Edit' : 'Add',
            fields     => [
                {
                    name => 'name',
                    optional => 0,
                    label => 'Name',
                    type => 'text',
                },
                {
                    name => 'street',
                    optional => 1,
                    label => 'Street',
                    type => 'text',
                },
                {
                    name => 'city',
                    optional => 1,
                    label => 'City',
                    type => 'text',
                },
                {
                    name => 'state',
                    optional => 1,
                    label => 'State',
                    type => 'text',
                },
                {
                    name => 'zip',
                    optional => 1,
                    label => 'Zip',
                    type => 'text',
                },
                {
                    name => 'phone',
                    optional => 1,
                    label => 'Phone',
                    type => 'text',
                },
            ],
        };
    } # END form
 
    #-----------------------------------------------------------------
    # get_model_name( )
    #-----------------------------------------------------------------
    sub get_model_name {
        return $ADDRESS;
    }
 
    #-----------------------------------------------------------------
    # get_orm_helper( )
    #-----------------------------------------------------------------
    sub get_orm_helper {
        return 'Gantry::Plugins::AutoCRUDHelper::DBIxClass';
    }
 
    #-----------------------------------------------------------------
    # text_descr( )
    #-----------------------------------------------------------------
    sub text_descr     {
        return 'address';

lib/Gantry/Docs/Tutorial.pod  view on Meta::CPAN

            label          Name;
            html_form_type text;
        }

This will be the name of one person in a nuclear family.

        field family {
            is                int4;
            label             Family;
            html_form_type    select;
            refers_to         address;
        }

This field becomes a foreign key pointing to the address table, since it
uses the C<refers_to> statement.  When the user enters a value for this
field, they must choose one family defined in the address table.

        field birthday {
            is                date;
            label             Birthday;
            html_form_type    date;
            date_select_text `Popup Calendar`;
        }
        foreign_display `%name`;
    }

I've chosen to store the actual date of birth (which leads to recording
women's ages, shame on me).  This is to show how date selection works
smoothly for your users.  There are three steps to this process.  The
first one is shown here: use the date_select_text statement.  Its value
becomes the link text the user clicks to popup the calendar selection
mini-window.  See, the controller below for the other two steps.

    controller Birth is AutoCRUD {
        controls_table   birth;
        rel_location     birthday;
        uses             Gantry::Plugins::Calendar;

Step two in easy dates is to use Gantry::Plugins::Calendar which provides
javascript code generation routines.

        text_description birthday;
        page_link_label  `Birth Day`;

This page will show up in site navigation with its page_link_label

        method do_main is main_listing {
            title            `Birth Day`;
            cols             name, family, birthday;
            header_options   Add;
            row_options      Edit, Delete;
        }

The main listing is just like the one for the address table, except for
the names of the displayed fields.

        method form is AutoCRUD_form {
            form_name        birthday_form;
            all_fields_but   id;
            extra_keys
                legend     => `$self->path_info =~ /edit/i ? 'Edit' : 'Add'`,
                javascript => `$self->calendar_month_js( 'birthday_form' )`;
        }
    }

Now the name of the form becomes important.  The calendar_month_js
method (mixed in by Gantry::Plugins::Calendar) generates the javascript
for the popup and its callback, which populates the date fields.
Note that we don't tell it which fields to handle.  It will work on
all fields that have date_select_text statements.

Once these changes are made, we can regenerate the application:

    bigtop docs/address.bigtop all

Execute this command while in the build directory (the one with the Changes
file in it).

For the app to work successfully, you will need to alter the existing
database so it has the new columns and birth day table.  Either throw out
the old database or alter it at your option.  Bigtop has data statements
which allow you to specify initial data for tables.  This makes discarding
a database less painful.

Again, I confess that I used tentmaker to get me started with the changes
above, then cleaned its output until it became the
L<Complete Bigtop Code Listing> below.

You can continue to edit the bigtop file with a text editor or tentmaker
and regenerate as the app matures.  We have regenerated production apps
months after deployment.

=head2 Complete Bigtop Code Listing

 config {
     engine CGI;
     template_engine TT;
     Init Std {  }
     SQL SQLite {  }
     SQL Postgres {  }
     SQL MySQL {  }
     CGI Gantry { gen_root 1; with_server 1; flex_db 1; }
     Control Gantry { dbix 1; }
     Model GantryDBIxClass {  }
     SiteLook GantryDefault {  }
 }
 app Apps::Address {
     config {
         dbconn `dbi:SQLite:dbname=app.db` => no_accessor;
         template_wrapper `genwrapper.tt` => no_accessor;
     }
     controller is base_controller {
         method do_main is base_links {
         }
         method site_links is links {
         }
     }
     table address {
         field id {
             is int4, primary_key, auto;
         }

lib/Gantry/Docs/Tutorial.pod  view on Meta::CPAN

         }
         field street {
             is varchar;
             label Street;
             html_form_type text;
             html_form_optional 1;
         }
         foreign_display `%name`;
         field city {
             is varchar;
             label City;
             html_form_type text;
             html_form_optional 1;
         }
         field state {
             is varchar;
             label State;
             html_form_type text;
             html_form_optional 1;
         }
         field zip {
             is varchar;
             label Zip;
             html_form_type text;
             html_form_optional 1;
             html_form_constraint `qr{^\d{5}$}`;
         }
         field country {
             is varchar;
             label Country;
             html_form_type text;
             html_form_optional 1;
         }
         field email {
             is varchar;
             label Email;
             html_form_type text;
             html_form_optional 1;
         }
         field phone {
             is varchar;
             label Phone;
             html_form_type text;
             html_form_optional 1;
         }
     }
     controller Address is AutoCRUD {
         controls_table address;
         rel_location address;
         text_description address;
         page_link_label Address;
         method do_main is main_listing {
             cols name, street;
             header_options Add;
             row_options Edit, Delete;
             title Address;
         }
         method form is AutoCRUD_form {
             all_fields_but id, created, modified;
             extra_keys
                 legend => `$self->path_info =~ /edit/i ? 'Edit' : 'Add'`;
         }
     }
     table birth {
         field id {
             is int4, primary_key, auto;
         }
         field name {
             is varchar;
             label Name;
             html_form_type text;
         }
         field family {
             is int4;
             label Family;
             refers_to address;
             html_form_type select;
         }
         field birthday {
             is date;
             label Birthday;
             html_form_type text;
             date_select_text `Popup Calendar`;
         }
         foreign_display `%name`;
     }
     controller Birth is AutoCRUD {
         controls_table   birth;
         rel_location     birthday;
         uses             Gantry::Plugins::Calendar;
         text_description birthdays;
         page_link_label `Birth Days`;
         method do_main is main_listing {
             title `Birth Day`;
             cols name, family, birthday;
             header_options Add;
             row_options Edit, Delete;
         }
         method form is AutoCRUD_form {
             form_name birthday_form;
             all_fields_but id;
             extra_keys
                 legend => `$self->path_info =~ /edit/i ? 'Edit' : 'Add'`,
                 javascript => `$self->calendar_month_js( 'birthday_form' )`;
         }
     }
 }

=head1 Summary

In this document we have seen how a simple Gantry app can be written
and deployed.  While building a simple app with bigtop can take just
a few minutes, interesting parts can be fleshed out as needed.  Our
goal is to provide a framework that automates the 50-80% of most apps
which is repetitive, allowing us to focus our time on the more interesting
bits that vary from app to app.

If you want to see a more realistic app, see Bigtop::Docs::Tutorial
which builds a basic freelancer's billing app.

There are other documents you might also want to read.

=over 4

=item Gantry::Docs::FAQ

categorized questions and answers explaining how to do common tasks

=item Gantry::Docs::About

marketing document listing the features of Gantry and telling its history

=back

The modules have their own docs which is where would be gantry developers
should look for more information.

=head1 Author

Phil Crow <philcrow2000@yahoo.com>

=head1 Copyright and License

Copyright (c) 2006-7, Phil Crow.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.

=cut



( run in 0.709 second using v1.01-cache-2.11-cpan-39bf76dae61 )