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

NAME

LIMS::Controller - Perl object layer controlling the LIMS database and its web interface

SYNOPSIS

        use LIMS::Controller;

        # login and session control
        my $database = database->new('My CGI Page');
        my $database = database->new_guest('My CGI Page');      # for pages where no user/pass required
        
        # embedded DBI and CGI objects
        my $dbh = database->get_dbh;
        my $q = database->get_cgi;

        # simplified database queries/inserts
        $database->sql_fetch_singlefield($statement);
        my $insert_id = $database->insert_into_table($table,$values);
        
        # error handling for DBI functions
        $database->kill_pipeline if ($database->any_error);
        
        # ....and it even tidies up after itself
        $database->finish;

DESCRIPTION

LIMS::Controller is a versatile object-oriented Perl module designed to control a LIMS database and its web interface. Inheriting from the LIMS::Web::Interface and LIMS::Database::Util classes, the module provides automation for many core and advanced functions required of a web/database object layer, enabling rapid development of Perl CGI scripts.

WRITING A LIMS::Controller PLUG-IN

First, look at the LIMS::ArrayPipeLine module. This is the plug-in written along-side LIMS::Controller, to control our laboratory's CGH-microarray LIMS database. There are many standard methods in there that you will probably want/need in your own module. For most situations, simply editing the config file (see below) to set defaults for your own system will suffice to provide you with a working LIMS.

SETTING UP YOUR DATABASE

There are several parameters that must be set in a config file. For our LIMS::ArrayPipeLine plug-in, running on a UNIX/LINUX type system, the path to this file is set as '/etc/pipeline.conf' and defined in LIMS::ArrayPipeLine. Most of the parameters are self-explanatory, including database hostname, database login, the base URL for the web server, etc.

The database being controlled by the module must have a table called 'USER_INFORMATION' as described in the accompanying documentation. This table handles user login at the WEB/CGI level. At the database level, a user should be defined with relevant privileges for all required WEB/CGI operations, and the user name and password for this account must be set in the config file. If, for some reason, you need to set other privilege levels beyond these, we suggest you do this at the WEB server level on a script-by-script basis. For instance, you might want to provide browse-only access to some members of staff, or reserve some admin functions for other members of staff.

METHODS

Basic functions

There are actually only a few methods that are used on a regular basis in a CGI script.

new, new_guest, new_script

All create a new LIMS::Controller object, but of subtly different flavours. new() creates the embedded DBI and CGI objects, and requires two form parameters; 'user_name', and either 'password' or 'session_id' which it verifies via the 'USER_INFORMATION' table in your database. The method new_guest() is similar but does not require the user parameters and does not verify login. The method new_script() returns a new object without CGI/DBI or user login. So the initial login page to the system would use the new_guest() method, and provide a form to enter a user_name and password. A script receiving a valid user_name/password combination will then return an object from the new() method, and create a valid session_id. Subsequently, the new() method will return an object from a valid user_name/session_id combination.

get_dbh, get_cgi

These methods return the embedded DBI database handle and CGI object respectively. The database handle should not be required, since most DBI functions are handled within LIMS::Controller. It is recommended that you use the object-oriented style of calling CGI methods, although you probably don't HAVE to.

is_back_sensitive

Prevents the user from using the back button on their browser by rejecting an old session_id.

is_unrepentant

Causes the script to die if any errors are thrown, printing out all errors and issuing a rollback call to the database.

finish

Tidies up at the end of a script; prints a page footer (if there is one), forwards parameters if not already performed, disconnects from the database and closes a log file (if there is one open).

page_title

Returns the page title, set in the new() and new_guest() methods.

DBI Functions

Most of these methods are simply wrappers for DBI calls, catching possible errors so that the way they are reported can be controlled in the CGI script. Why not use Class::DBI? Well you can if you prefer - table classes are already loaded via Class::DBI::Loader.

Simple SQL 'fetch' methods

Methods fetching the results of SELECT queries are offered in a variety of flavours, returning results in different Perl data structures. Pass these methods a string 'SELECT' query.

sql_fetch_singlefield

Return a single row, single value, as a scalar

sql_fetch_multisinglefield

Multiple rows of a single value are returned as an array reference

sql_fetcharray_singlerow

Wrapper for the DBI method fetchrow_arrayref(). A single row of multiple values is returned as an array reference

sql_fetchlist_singlerow

Wrapper for the DBI method fetchrow_array(). A single row of multiple values is returned as an array

sql_fetcharray_multirow

Wrapper for the DBI method fetchall_arrayref(). Multiple rows of multiple values are returned as a reference to a 2D array

sql_fetch_twofieldhash

Special use; If you have a query that returns a row of two values, where the first value is a primary key or other unique index. This method will return a reference to a hash whose keys/values are the first/second values of each row

sql_fetchhash_singlerow

Wrapper for the DBI method fetchrow_hashref(). A single row of multiple values is returned as a reference to a hash, whose keys are the column names, and whose values are the row values

sql_fetchhash_multirow

Multiple rows of multiple values are returned as a reference to an array of hashes

retrieve_file_blob

Utilises sql_fetch_singlefield(), but sets the DBI {LongReadLen} variable to a default value of 32Mb in order to return long BLOB fields

SQL Insert and Update methods

Please note: LIMS::Controller does not control database privileges - it is assumed that the database login used by the module is only Grant[ed] privileges necessary/suitable for your application. Therefore, if the login does not have update/insert privileges, these methods will return relevant database errors caught by DBI.

insert_into_table

Pass this method the table name together with the values to be inserted as a pre-formatted string, in the correct table column order. The method will generate the field values which will not include an auto-increment primary key. If you need/want to specify the field values, you can pass them as a pre-formatted string after the insert values - in which case the order of the two should match but do not have to be the table column order

insert_with_placeholders

Use this method for inserts of multiple rows of data, or if you need to insert quoted strings. This method is called similar to insert_into_table(), except that the values are passed as a reference to a 2D array of values to be inserted. If you need to pass the table fields, then you also need to pass a string of the correct number of placeholders. OK, so this isn't so tidy, but I was lost for a better way to do it.

Unlike other methods, this will call kill_pipeline() if any errors are caught, or commit_session() upon successful completion of all inserts. If the insert statament returns an insert_id, and the call to this method requests an array, it will return a list of the insert_ids created by each executed statement. Otherwise, it will return the number of inserted rows.

        my $aaValues = [
                [ $value1, $value2 ],
                [ $value3, $value4 ]
        ];
        $database->insert_with_placeholders($table,$aaValues);
        $database->insert_with_placeholders($table,$aaValues,"field1,field2","?,?");
simple_update_placeholders

Use this method to update a single value in a table row. Pass the table name, the field to be set, the new value, and finally a 'WHERE' clause.

CGI methods

param_forward

Forwards all current parameters as hidden values. (Hidden in a '4-year old playing hide-and-seek' kind of way - in the HTML).

min_param_forward

Forwards only 'user_name' and 'session_id' parameters as hidden values

format_url_base_query

Formats 'user_name' and 'session_id' parameter values to append to a cgi script's url

format_redirect

Pass a script name to format a url to the script with 'user_name' and 'session_id' parameter values

format_redirect_full

Pass a script name to format a url to the script with all parameters

javascript

Creates a <script> tag in the HTML header for defining Javascript code. You can pass either an array ref containing one or more URLs to javascript files, or a HERE string of formatted javascript code.

Handling Errors

One of the main reasons for writing this module was because I wanted to be able to deal with all errors - Perl, CGI, DBI - in a more efficient manner, all at the same time. If you want your script to die straight away printing relevant errors to the web page, you can set the object to be 'unrepentant' as described above. The default is that the object allows you to be sorry for your coding sins, and then explains nicely what's gone wrong.

Three methods, db_error(), standard_error() and any_error() handle the errors for us, and the kill_pipeline() method prints them out upon killing the script; db_error() returns any database (DBI) errors that have been caught; standard_error() can be used to set any error/complaint in a CGI script, or returns any standard_error that has already been set; while any_error() returns true if errors of any type have been caught. So one line of code handles most eventualities;

        $database->kill_pipeline if ($database->any_error);  

If you have a simple situation where you want to kill the script with an error you've caught in your script, you can combine the error with the kill_pipeline() method;

        $database->kill_pipeline('got a problem');

Errors can be returned in text (rather than HTML) format by calling the method text_errors(), or printed separately without calling kill_pipeline() using the print_db_errors(), print_standard_errors() or print_errors() methods. If you need to, you can clear errors using the methods clear_db_errors(), clear_standard_errors() or clear_all_errors().

SEE ALSO

LIMS::Base, LIMS::Web::Interface, LIMS::Database::Util, LIMS::ArrayPipeLine

AUTHORS

Christopher Jones and James Morris, Translational Research Laboratories, Institute for Women's Health, University College London.

http://www.instituteforwomenshealth.ucl.ac.uk/trl

c.jones@ucl.ac.uk, james.morris@ucl.ac.uk

COPYRIGHT AND LICENSE

Copyright 2007 by Christopher Jones

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