The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

CatalystX::ListFramework::Builder - *** DEPRECATED *** please see Catalyst::Plugin::AutoCRUD

VERSION

This document refers to version 0.42 of CatalystX::ListFramework::Builder

WARNING

This module will work, but is no longer actively being developed. The same author has created Catalyst::Plugin::AutoCRUD which should be almost a drop-in replacement for you, and has even more yummy automagic goodness.

PURPOSE

You have a database, and wish to have a basic web interface supporting Create, Retrieve, Update, Delete and Search, with little effort.

This module, with only a few lines of configuration, is able to create such interfaces on the fly. They are a bit whizzy and all Web 2.0-ish.

SYNOPSIS

A configuration file somewhere on your system:

 # [listframeworkuser.conf] in Config::General format
 
 extjs2   /static/javascript/extjs-2
 
 <Model::LFB::DBIC>
     schema_class   My::Database::Schema
     connect_info   dbi:Pg:dbname=mydbname;host=mydbhost.example.com;
     connect_info   username
     connect_info   password
     <connect_info>
         AutoCommit   1
     </connect_info>
 </Model::LFB::DBIC>

And in the CGI area of your web server:

 package ListFrameworkUser;
 use Catalyst qw(ConfigLoader +CatalystX::ListFramework::Builder);
 
 __PACKAGE__->setup;
 1;

Now going to the CGI area's URL will display a list of the tables in your database. Each item is a link to the web interface for that table.

DESCRIPTION

This module contains an application which will automatically construct a web interface for a database on the fly. The web interface supports Create, Retrieve, Update, Delete and Search operations.

The interface is not written to static files on your system, and uses AJAX to act upon the database without reloading your web page (much like other Web 2.0 appliactions, for example Google Mail).

Almost all the information required by the application is retrieved from the DBIx::Class ORM frontend to your database, which it is expected that you have already set up (although see "USAGE", below). This means that any change in database schema ought to be reflected immediately in the web interface after a page refresh.

USAGE

Pre-configuration

You'll need to download the ExtJS Javascript Library (version 2.2+ recommended), from this web page: http://extjs.com/products/extjs/download.php.

Install it to your web server in a location that it is able to serve as static content. Make a note of the path used in a URL to retrieve this content, as it will be needed in the application configuration file, below.

Scenario 1: Plugin to an existing Catalyst App

This mode is for when you have written your Catalyst application, but the Views are catering for the users and as an admin you'd like a more direct, secondary web interface to the database.

 package ListFrameworkUser;
 use Catalyst qw(ConfigLoader +CatalystX::ListFramework::Builder);
 
 __PACKAGE__->setup;
 1;

Adding CatalystX::ListFramework::Builder (LFB) as a plugin to your Catalyst application, as above, causes it to scan your existing Models. If any of them are built using Catalyst::Model::DBIC::Schema, they are automatically loaded. You still need to provide a small amount of configuration:

 extjs2   /static/javascript/extjs-2
 <Controller::LFB::Root>
     <action>
         <base>
             PathPart   admin
         </base>
     </action>
 </Controller::LFB::Root>

First the application needs to know where your copy of ExtJS is, on the web server. Use the extjs2 option as shown above to specify the URL path to the libraries. This will be used in the templates in some way like this:

 <script type="text/javascript" src="[% c.config.extjs2 %]/ext-all.js" />

In the above example, the path .../admin/ will contain the LFB application, and all generated links in LFB will also make use of that path. Remember this is added to the base of your Cataylst application which, depending on your web server configuration, might also have a leading path.

This mode of operation works even if you have more than one database. You will be offered a Home screen to select the database, and then another menu to select the table within that.

Scenario 2: Frontend for an existing DBIx::Class::Schema based class

In this mode, CatalystX::ListFramework::Builder (LFB) is running standalone, in a sense as the Catalyst application itself. Your main application file looks the same as in Scenario 1, though:

 package ListFrameworkUser;
 use Catalyst qw(ConfigLoader +CatalystX::ListFramework::Builder);
 
 __PACKAGE__->setup;
 1;

For the configuration, you need to tell LFB which package contains the DBIx::Class schema, and also provide database connection parameters.

 extjs2   /static/javascript/extjs-2
 <Model::LFB::DBIC>
     schema_class   My::Database::Schema
     connect_info   dbi:Pg:dbname=mydbname;host=mydbhost.example.com;
     connect_info   username
     connect_info   password
     <connect_info>
         AutoCommit   1
     </connect_info>
 </Model::LFB::DBIC>

First the application needs to know where your copy of ExtJS is, on the web server. Use the extjs2 option as shown above to specify the URL path to the libraries. This will be used in the templates in some way like this:

 <script type="text/javascript" src="[% c.config.extjs2 %]/ext-all.js" />

The Model::LFB::DBIC section must look (and be named) exactly like that above, except you should of course change the schema_class value and the values within connect_info.

DBIx::Class setup

You will of course need the DBIx::Class schema to be created and installed on your system. The recommended way to do this quickly is to use the excellent DBIx::Class::Schema::Loader module which connects to your database and writes DBIx::Class Perl modules for it.

Pick a suitable namespace for your schema, which is not related to this application. For example DBIC::Database::Foo::Schema for the Foo database (in the configuration example above we used My::Database::Schema). Then use the following command-line incantation:

 perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:. -e \
     'make_schema_at("DBIC::Database::Foo::Schema", { debug => 1 }, \
     ["dbi:Pg:dbname=foodb;host=mydbhost.example.com","user","pass" ])'

This will create a directory (such as DBIC) which you need to move into your Perl Include path (one of the paths shown at the end of perl -V).

Scenario 3: Lazy loading a DBIx::Class schema

If you're in such a hurry that you can't create the DBIx::Class schema, as shown in the previous section, then CatalystX::ListFramework::Builder (LFB) is able to do this on the fly, but it will slow the application down a little.

The application file and configuration are very similar to those in Scenario 2, above, except that you omit the schema_class configuration option because you want LFB to generate that on the fly (rather than reading an existing one from disk).

 package ListFrameworkUser;
 use Catalyst qw(ConfigLoader +CatalystX::ListFramework::Builder);
 
 __PACKAGE__->setup;
 1;

 extjs2   /static/javascript/extjs-2
 <Model::LFB::DBIC>
     connect_info   dbi:Pg:dbname=mydbname;host=mydbhost.example.com;
     connect_info   username
     connect_info   password
     <connect_info>
         AutoCommit   1
     </connect_info>
 </Model::LFB::DBIC>

When LFB loads it will connect to the database and use the DBIx::Class::Schema::Loader module to reverse engineer its schema. To work properly you'll need the very latest version of that module (0.05 or greater).

The other drawback to this scenario (other than the slower operation) is that you have no ability to customize how foreign, related records are shown. A related record will simply be represented as something approximating the name of the foreign table, the names of the primary keys, and associated values (e.g. id(5)).

TIPS AND TRICKS

When the web interface wants to display a column which references another table, you can make things look much better by adding a custom render method to your DBIx::Class Result Classes (i.e. the class files for each table).

First, the application will look for a method called display_name and use that. Here is an example which could be added to your Result Class files below the line which reads DO NOT MODIFY THIS OR ANYTHING ABOVE, and in this case returns the data from the title column:

 sub display_name {
     my $self = shift;
     return $self->title || '';
 }

Failing the existence of a display_name method, the application attempts to stringify the row object. Using stringification is not recommended, although some people like it. Here is an example of a stringification handler:

 use overload '""' => sub {
     my $self = shift;
     return $self->title || '';
 }, fallback => 1;

If all else fails the application prints the best hint it can to describe the foreign row. This is something approximating the name of the foreign table, the names of the primary keys, and associated values. It's better than stringifying the object the way Perl does, anyway.

Columns with auto-increment data types

For those columns where your database uses an auto-incremented value, add the is_auto_increment => 1, option to the relevant hash in add_columns(). This will let the application know you don't need to supply a value for new or updated records. The interface will look much better as a result.

Database IO filters

Buried within one of the modules in this application are some filters which are applied to data of certain types as it enters or leaves the database. If you find a particular data type is not being rendered correctly, please drop the author a line at the email address below, explaining what you'd like to see instead.

Relocating LFB to another URL path

If you want to use this application as a plugin with another Catalyst system, it should work fine, but you probably want to serve pages under a different path on your web site. To do that, add the following to your configuration file:

 <Controller::LFB::Root>
     <action>
         <base>
             PathPart   admin
         </base>
     </action>
 </Controller::LFB::Root>

In the above example, the path .../admin/ will contain the LFB application, and all generated links in LFB will also make use of that path. Remember this is added to the base of your Cataylst application which, depending on your web server configuration, might also have a leading path.

EXAMPLES

The code examples give above in this manual are also supplied in the form of a sample application. You'll find the application itself in the examples/app/ directory of this distribution, and the SQLite3 data source in the examples/sql/ directory.

INSTANT DEMO APPLICATION

If you want to run an instant demo of this module, with minimal configuration, then a simple application for that is shipped with this distribution. For this to work, you must have the very latest version of DBIx::Class::Schema::Loader installed on your system (> 0.04005).

First go to the examples/demo/ directory of this distribution and edit demo.conf so that it contains the correct dsn, username, and password for your database. Next, download a copy of the ExtJS 2.x Javascript library, and make a note of where you put it. Then create the following directory, and symbolic link:

 demo> mkdir -p root/static
 demo> ln -s /path/to/your/extjs-2 root/static/extjs-2

Now start the demo application like so:

 demo> perl ./server.pl

Although the instruction at the end of the output says to visit (something like) http://localhost:3000, you must instead visit http://localhost:3000/lfb/ (i.e. add /lfb/ to the end).

LIMITATIONS

Single column primary key

There's no support for multiple column primary keys (composite/compound keys). This has saved a lot of time in development because it greatly simplifies the Catalyst and DBIx::Class code.

No two columns in a given table may have the same FK constraint

If you have two columns which both have foreign key constraints to the same table, it's very likely LFB will not work. Again this is a simplification which speeded the initial development.

For the issues above, if you're desperate that the feature be implemented soon, please drop me a line at the address below, because you might be able to buy some of my time for the development.

REQUIREMENTS

  • ExtJS Javascript Library (version 2.2+ recommended), from http://extjs.com.

  • Catalyst::Runtime >= 5.70

  • Catalyst::Model::DBIC::Schema

  • Catalyst::View::JSON

  • Catalyst::View::TT

  • Catalyst::Action::RenderView

  • MRO::Compat

SEE ALSO

CatalystX::CRUD and CatalystX::CRUD:YUI are two distributions which allow you to create something similar but with full customization, and the ability to add more features. So, you trade effort for flexibility and power.

CatalystX::ListFramework is similar but has no dependency on Javascript (though it can use it for fancy auto-complete searches), and it also allows you to control which columns are rendered in the display.

ACKNOWLEDGEMENTS

Without the initial work on CatalystX::ListFramework by Andrew Payne and Peter Edwards this package would not exist. If you are looking for something like this module but without the dependency on Javascript, please do check out CatalystX::ListFramework.

AUTHOR

Oliver Gorwits <oliver.gorwits@oucs.ox.ac.uk>

COPYRIGHT & LICENSE

Bundled images are Copyright (c) 2006 Mark James, and are from http://www.famfamfam.com/lab/icons/silk/.

This distribution ships with the Ext.ux.form.DateTime Extension Class for Ext 2.x Library, Copyright (c) 2008, Ing. Jozef Sakalos, and released under the LGPL 3.0 license (library version 289, 2008-06-12 21:08:08).

The rest is Copyright (c) Oliver Gorwits 2008.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.