NAME

CatalystX::ListFramework - foundations for displaying and editing lists (CRUD) in a Catalyst app.

SYNOPSIS

package MyApp::Controller::Root;
use base 'Catalyst::Controller';
use CatalystX::ListFramework;

sub listsearch :Path('/listsearch') {
    my ($self, $c, $kind) = @_;
    my $lf = CatalystX::ListFramework->new($kind, $c);
    my $restrict = {};
    $lf->stash_listing('myview', 'myprefix', $restrict);
    $c->stash->{template} = 'list-and-search.tt';
}

sub detail :Path('/get') {
    my ($self, $c, $kind, $id) = @_;
    my $lf = CatalystX::ListFramework->new($kind, $c);
    $lf->stash_infoboxes({'me.id' => [{'=' => $id}]}); 
    $c->stash->{kind} = $kind;
    $c->stash->{id} = $id;  # the update form adds it to the URL
    $c->stash->{template} = 'detail.tt';
}

sub update :Path('/update') {
    my ($self, $c, $kind, $id) = @_;
    my $lf = CatalystX::ListFramework->new($kind, $c);
    $lf->update_from_query({'me.id' => [{'=' => $id}]}); 
    $c->res->redirect("/listsearch/$kind");
}

sub create :Path('/create') {
    my ($self, $c, $kind) = @_;
    my $lf = CatalystX::ListFramework->new($kind, $c);
    my $id = $lf->create_new; 
    $c->res->redirect("/get/$kind/$id");
}

DESCRIPTION

Displaying tabulated lists of database records, updating those records and creating new ones is a common task in Catalyst applications. This class supplies such lists, and forms to edit such records, to a set of templates, using simple definition files and your DBIx::Class Catalyst model. A search form is also supplied, which can include JSON-powered ExtJS comboboxes (see www.extjs.com).

To run the included demo application, grab a copy of ExtJS, then

cd t/
ln -s /path/to/extjs/ static/extjs-1.1
lib/script/testapp_server.pl

then

firefox http://localhost:3000/start

Please see BUGS about some SQLite issues with the demo app. The noninteractive test suite is

perl live-test.pl

DEFINITION FILES

ListFramework is driven by a set of definition files, found under formdef/, one pair per schema class (table). These are divided into 'master' files and 'site' files and are named kind.form. Files under master/ describe a kind's source, what fields it has available, and how it is associated with other schema classes. Files under site/ describe how the data is displayed on the page. This division, and the naming, implies that a vendor could supply the master files, while a particular installation could use customised site files to suit their needs.

These are best understood by looking at the example files.

Files under /master

The sections in these files are:

title

A title, displayed on various screens.

model

The DBIx::Class model to use.

uses

This is a hashref linking this schema to others, in the form field => 'kind'. In site files, you can then use field. (or several ones nested) to access the foreign schema, for example "fromalbum.artist.name". field must be listed in the schema as a column and have a matching belongs_to relationship.

columns

This hashref species what columns the schema makes available and provides some metadata, such as column headings, default values and types.

The 'field' property may be an arrayref, all elements of which are concatenated for display. Static text can be specified using a scalar ref, and you can call functions from the Helper class by specifying function(field). For example,

field => [ \'(', uc(surname), \')' ]

If a 'type' field is specified', then a coresponding filter function in Helper::Types is called.

There should be a special entry, OBJECT => {primary_key => 'id'}, which specifies the table's primary key (and has other uses). Referencing OBJECT in a site file gives you the serialisation of the object, which is useful if you've overloaded "", as in:

package TestApp::Model::TestModel::Artist;
use overload '""' => sub {
    my $self = shift;
    return $self->artist_forename . ' ' . $self->artist_surname;
};
create_uri
delete_uri

These are simply read by the template. If they're specified, then links are generated on the page.

searches

This is a hashref of searches which are made available by this schema, e.g.

albtitle => {heading=>'Album title', field=>'title', op=>'like'}

If 'op' is set to 'like', the user's input is automatically surrounded by '%'s. The other usual choice is '='.

Files under /site

The sections in these files are:

display

This is a hashref of views, each of which is an arrayref of columns to show in a list.

default => [
              {id=>'tid', heading => 'Track Code', uri => '/get/track/'},
              ...
           ]
           

'id' can refer to foreign fields through the dot notation. If 'uri' is specified, then the value of the entry's primary key is appended and the column is shown as a link.

This is an arrayref of fields you want the search form to present (in order).

{id=>'fromalbum.albid', autocomplete=>['fromalbum.id' => 'fromalbum.title'], minchars=>'0'}

The 'autocomplete' parameter takes 2 arguments in an arrayref: a hidden field (sent when a search form is submitted) and a field to be displayed to the user in a dropdown list. Here, album titles are shown, but album IDs are sent by the form. The user can find an album by typing a substring.

You can also override the 'heading' parameter from the 'site' .form file.

infoboxes

The detail view for an entry is split into 'boxes', which are rendered as ExtJS tabs in the demo app. You can specify local or foreign schema fields by the 'id' property, and headings etc can be overridden as usual. All fields are editable, unless 'not_editable' is true. If '.OBJECT' is specified, a dropdown list of choices is presented.

track => [
    {id => 'ttitle', not_editable => 1},
    {id => 'fromalbum.artist.OBJECT', heading=>'Who by'},
    ...
    
infobox_order

TEMPLATES

JSON

BUGS

Something somewhere between SQLite and DBIx::Class is buggy when it comes to row updates. You will see TT error screens on the demo app about "too many rows updated". This goes away if you refresh and is fine with a real DB like MySQL.

AUTHOR

Andrew Payne <andrew@dragonstaff.com>.

COPYRIGHT

This module is Copyright (C) 2007 Dragonstaff Ltd and is licensed under the same terms as Perl itself.