NAME
Class::Tables - Auto-vivification of persistent classes, based on RDBMS schema
SYNOPSIS
Telling your relational object persistence class about all your table relationships is no fun. Wouldn't it be nice to just include a few lines in a program:
use Class::Tables;
Class::Tables->dbh($dbh);
and magically have all the object persistence classes from the database, preconfigured, with table relations auto-detected, etc?
This is the goal of Class::Tables. Its aim is not to be an all-encompassing tool like Class::DBI, but to handle the most common and useful cases smartly, quickly, and without needing your help. Just pass in a database handle, and this module will read your mind (by way of your database's table schema) in terms of relational object persistence. The very simple (and flexible) rules it uses to determine your object relationships from your database's schema are so simple, you will probably find that you are already following them.
Introductory Example
Suppose your database schema were as unweildy as the following MySQL. The incosistent naming, the plural table names and singular column names are not a problem for Class::Tables.
create table departments (
    id                int not null primary key auto_increment,
    department_name   varchar(50) not null
);
create table employees (
    employee_id       int not null primary key auto_increment,
    name              varchar(50) not null,
    salary            int not null,
    department_id     int not null
);
To use Class::Tables, you need to do no more than this:
use Class::Tables;
my $dbh = DBI->connect($dsn, $user, $passwd) or die;
Class::Tables->dbh($dbh);
Et voila! Class::Tables looks at your table schema and generates two classes, Departments and Employees, each with constructor and search class methods:
my $marketing  = Departments->new( name => "Marketing" );
my @underpaid  = Employees->search( salary => 20_000 );
my @marketeers = Employees->search( department => $marketing );
my $self       = Employees->fetch($my_id);
It also generates the following instance methods:
- A deletion method for both classes
 - 
This simply removes the object from the database.
$marketing->delete; - Readonly id accessor methods for both classes
 - 
For
Departmentsobjects, this corresponds to theidcolumn in the table, and forEmployeesobjects, this corresponds to theemployee_idcolumn. Class::Tables is smart enough to figure this out, even though "employee" is singular and "employees" is plural (See "Plural And Singular Nouns").print "You're not just a name, you're a number: " . $self->id; - Normal accessor/mutator methods
 - 
Departmentsobjects get anameaccessor/mutator method, andEmployeesobjects getnameandsalaryaccessor/mutator methods, referring to the respective columns in the database. Note that thedepartment_prefix is automatically removed from thedepartment_namecolumn because the name of the table isdepartments.$self->salary(int rand 100_000); print "Pass go, collect " . $self->salary . " dollars"; - Foreign key methods
 - 
When Class::Tables sees the
department_idcolumn in theemployeestable, it knows that there is also adepartmentstable, so it treats this column as a foreign key. Thus,Employeesobjects get adepartmentaccessor/mutator method, which returns (and can be set to) aDepartmentsobject.print "I'd rather be in marketing than " . $self->department->name; $self->department($marketing);It also reverses the foreign key relationship, so that all
Departmentsobjects have a readonlyemployeesmethod, which returns a list of allEmployeesobjects referencing the particularDepartmentsobject.my @overpaid = $marketing->employees; ## same as: Employees->search( department => $marketing ) my @coworkers = $self->department->employees;Notice how the plural vs. singular names of the methods match their return values. This is all automatic! (See "Plural And Singular Nouns")
 
USAGE
Class::Tables offers more functionality than just the methods in this example.
Database Metadata
Here's a more concrete explanation of how Class::Tables will use your table schema to generate the persistent classes.
- Class Names
 - 
Each table in the database must be associated with a class. The table name will be converted from
underscore_separatedstyle intoStudlyCapsfor the name of the class/package. - Primary Key
 - 
All tables must have a integer single-column primary key. By default, Class::Tables will use the primary key from the table definition. If no column is explicitly listed as the primary key, it will try to find one by name: valid names are
idor the table name (plus or minus pluralization) followed by an_idsuffix.In our above example, if the
primary keykeyword was omitted from theemployeestable definition, Class::Tables would have looked for columns namedemployee_id,employees_id, oridas the primary key. The flexibility allows for reasonable choices whether you name your tables as singular or plural nouns. (See "Plural And Singular Nouns")Note: For simplicity and transparency, the associated object accessor is always named
id, regardless of the underlying column name.In MySQL, the primary key column must be set to
AUTO_INCREMENT.In SQLite, the primary key may be an auto increment column (in SQLite this is only possible if the column is the first column in the table and declared as
integer primary key) using the same naming conventions as above. Alternatively, you may omit an explicit primary key column and Class::Tables will use the hiddenROWIDcolumn.In Postgres, the primary key column must be a
serial primary key. Using the hiddenoidcolumn as primary key is not (yet) supported. - Foreign Key Inflating
 - 
If a column has the same name as another table (plus or minus pluralization), that column is treated as a foreign key reference to that table. The column name may also have an optional
_idsuffix andtablename_prefix, wheretablenameis the name of the current table (plus or minus pluralization). The name of the accessor is the name of the column, without the optional prefix and suffix.In our above example, the foreign key column relating each employee with a department could have been anything matching
/^(employees?_)?(departments?)(_id)?$/, with the accessor being named the value of $2 in that expression. Again, the flexibility allows for a meaningful column name whether your table names are singular or plural. (See "Plural And Singular Nouns").The foreign key relationship is also reversed as described in the example. The name of the accessor in the opposite 1-to-1ion is the name of the table. In our example, this means that objects of the
Departmentsclass get an accessor namedemployees. For this reason, it is often convenient to name the tables as plural nouns. - Lazy Loading
 - 
All
*bloband*textcolumns will be lazy-loaded: not loaded from the database until their values are requested or changed. - Automatic Sort Order
 - 
The first column in the table which is not the primary key is the default sort order for the class. All operations that return a list of objects will be sorted in this order (ascending). In our above example, both tables are sorted on
name. - Stringification
 - 
If the table has a
nameaccessor, then any objects of that type will stringify to the value of$obj->name. Otherwise, the object will stringify toCLASS:ID. 
Public Interface
use Class::Tables %args- 
Valid argument keys are:
- cascade
 - 
Takes a boolean value indicating whether to perform cascading deletes. See
deletebelow for information on cascading deletes. If you need to change cascading delete behavior on the fly, localize$Class::Tables::CASCADE. - inflect
 - 
Takes a boolean value indicating whether to use Lingua::EN::Inflect for plural & singular nouns. See "Plural And Singular Nouns" for more information on noun pluralization.
 - cache
 - 
Takes a filename argument of a schema cache. This speeds up slow databases and large schemas. It uses Storable to save the results of the schema mapping, and on each subsequent execution, uses the cache to keep from doing the mapping again. If your database's schema changes, simply empty the cache file to force a re-mapping.
You can omit this arg or pass a false value to disable this feature.
 
The default behavior is:
use Class::Tables cascade => 1, inflect => 1, cache => undef; Class::Tables->dbh($dbh)- 
You must pass Class::Tables an active database handle before you can use any generated object classes.
 
Object Instance Methods
Every object in a Class::Tables-generated class has the following methods:
$obj->id- 
This readonly accessor returns the primary key of the object.
 $obj->delete- 
Removes the object from the database. The behavior of further method calls on the object are undefined.
If cascading deletes are enabled, then all other objects in the database that have foreign keys pointing to
$objare deleted as well, and so on. Cyclic references are not handled gracefully, so if you have a complicated database structure, you should disable cascading deletes. You can roll your own cascading delete (to add finer control) very simply:package Department; sub delete { my $self = shift; $_->delete for $self->employees; $self->SUPER::delete; }It's important to point out that in this process, if an object looses all foreign key references to it, it is not deleted. For example, if all Employees in a certain department are deleted, the department object is not automatically deleted. If you want this behavior, you must add it yourself in the Employees::delete method.
 $obj->attriband$obj->attrib($new_val)- 
For normal columns in the table (that is, columns not determined to be a foreign key reference), accessor/mutator methods are provided to get and set the value of the column, depending if an argument is given.
For foreign key reference columns, calling the method as an accessor is equivalent to a
fetch(see below) on the appropriate class, so will return the referent object orundefif there is no such object. When called as a mutator, the argument may be either an ID or an appropriate object:## both are ok: $self->department( $marketing ); $self->department( 10 );For the reverse-mapped foreign key references, the method is readonly, and returns a list of objects. It is equivalent to a
search(see below) on the appropriate class, which means you can also pass additional constraints:my @marketeers = $marketing->employees; ## same as Employees->search( department => $marketing ); my @volunteers = $marketing->employees( salary => 0 ); ## same as Employees->search( department => $marketing, salary => 0 );For all columns in the table
tablename, the column name will have anytablename_prefix removed in the name of the accessor (plus or minus pluralization). So in the Employees table, the column name is effectively treated withs/^employees?_//before consideration. $obj->field($field)and$obj->field($field, $new_val)- 
This is an alternative syntax to accessors/mutators. When
$fieldis the name of a valid accessor/mutator method for the object, this is equivalent to saying$obj->$fieldand$obj->$field($new_val). $obj->field- 
With no arguments, the
fieldaccessor returns a list of all the data accessors for that type of object. It's the same idea as CGI'sparammethod with no arguments.for my $accessor ($obj->field) { printf "$accessor : %s\n", scalar $obj->$accessor; } $obj->dump- 
Returns a hashref containing the object's attribute data. It recursively inflates foreign keys and maps reverse foreign keys to an array reference. This is particularly useful for generating structures to pass to HTML::Template and friends.
As an example, suppose we also have tables for Purchases and Products, with appropriate foreign keys. Then the result of
dumpon an Employees object may look something like this:{ 'name' => 'John Doe', 'id' => 7, 'department.name' => 'Sales', 'department.id' => 4, 'purchases' => [ { 'date' => '2002-12-13', 'quantity' => 1, 'id' => 5, 'product.id' => 1, 'product.name' => 'Widgets', 'product.price' => 200, 'product.weight' => 10 }, { 'date' => '2002-12-15', 'quantity' => 2, 'id' => 6, 'product.id' => 3, 'product.name' => 'Foobars', 'product.price' => 150, 'product.weight' => 200 } ] };The amount of foreign key inflation is bounded: A foreign key accessor will only be followed if its corresponding table hasn't been seen before in the dump. This is necessary because, for example, our Purchases objects have a foreign key pointing back to the John Doe employee object. But following that foreign key would cause an infinite recursion. This means there is no quick way to get a list such as
$john_doe->department->employeesfrom the dump, because the employees table would be visited twice. 
Data Class Methods
Every persistent object class that Class::Tables generates gets the following class methods:
Class->new( field1 => $value1, field2 => $value2, ... )- 
Creates a new object in the database with the given attributes set. If successful, returns the object, otherwise returns
undef. This is equivalent to the following:my $obj = Class->new; $obj->field1($value1); $obj->field2($value2); ...So for foreign key attributes, you may pass an actual object or an ID:
## both are ok: my $e = Employees->new( department => $marketing ); my $e = Employees->new( department => 10 ); Class->search( field1 => $value1, field2 => $value2, ... )- 
Searches the appropriate table for objects matching the given constraints. In list context, returns all objects that matched (or an empty list if no objects matched). In scalar context returns only the first object returned by the query (or
undefif no objects matched). The scalar context SQL query is slightly optimized.field1,field2, etc, must be names of the accessors, and not the underlying column.As usual, for foreign key attributes, you may pass an actual object or an ID. If no arguments are passed to
search, every object in the class is returned. Class->fetch($id)- 
Equivalent to
Class->search( id => $id )in scalar context, but slightly optimized internally. Returns the object, orundefif no object with the given ID exists in the database. 
Notes On Persistent Classes
Objects in these persistent classes are implemented as lightweight blessed scalars in an inside-out mechanism. This has some benefits, mainly that concurrency across identical objects is always preserved:
my $bob1 = Employees->fetch(10);
my $bob2 = Employees->fetch(10);
## now $bob1 and $bob2 may not be the same physical object, but...
$bob1->name("Bob");
$bob2->name("Robert");
print $bob1->name, $bob2->name;
## will print "Robert" twice
You can still override/augment object methods if you need to with SUPER::
## Suppose the "last_seen" column in a "users" table was a
## YYYYMMDDHHMMSS timestamp column. We could override the last_seen
## method to return a Time::Piece object, and accept one when used
## as a mutator:
package Users;
my $date_fmt = "%Y%m%d%H%M%S";
sub last_seen {
    my $self = shift;
    my $ret  = @_
      ? $self->SUPER::last_seen( $_[0]->strftime($date_fmt) );
      : $self->SUPER::last_seen;
    Time::Piece->strptime($ret, $date_fmt);
}
But since these objects are implemented as blessed scalars, you have to use some sort of inside-out mechanism to store extra (non-persistent) subclass attributes with the objects:
package Employees;
my %foo;
sub foo {
    my $self = shift;
    @_ ? $foo{ $self->id } = shift
       : $foo{ $self->id };
}
sub DESTROY {
    my $self = shift;
    delete $foo{ $self->id };
    $self->SUPER::DESTROY;
}
Subclassing/wrapping Class::Tables
You may find it necessary to subclass Class::Tables. One example I can think of is to implement a security wrapper. This is pretty simple. You can simply add wrappers to the new, fetch, and search methods as necessary. For a really hairbrained example, say that we want to restrict certain users of our application to objects with odd-numbered IDs only:
package MySubclass;
use base 'Class::Tables';
sub fetch {
    my ($pkg, $id) = @_;
    warn("Odd numbers only!") and return
        unless privileged( get_current_user() ) or $id % 2;
    $pkg->SUPER::fetch($id);
}
sub search {
    my $pkg = shift;
    my @results = $pkg->SUPER::search(@_);
    ## something similarly appropriate if the current user is
    ## unprivileged, perhaps along the lines of:
    ##     grep { $_->id % 2 } @results
    ## or raising warnings, etc.
}
To use this subclass, simply change these two lines in the application code and the persistence classes will be created beneath MySubclass instead of beneath Class::Tables:
## use Class::Tables;
## Class::Tables->dbh($dbh);
use MySubclass;
MySubclass->dbh($dbh);
## this will now raise a warning if privileges not met
my $obj = Employees->fetch(10);
Plural And Singular Nouns
Class::Tables makes strong use of Lingua::EN::Inflect to convert between singular and plural, in an effort to make accessor names more meaningful and allow a wide range of column-naming schemes. So when this documentation says "plus or minus pluralization", it does not just consider "adding an S at the end." You zoologists may have a mice table with a corresponding primary/foreign key named mouse_id! Goose to geese, child to children, etc. The only limitations are what Lingua::EN::Inflect doesn't know about.
I recommend naming tables with a plural noun and foreign key columns with a singular noun (optionally with the _id suffix). This combination makes the accessor names much more meaningful, and is (to my knowledge) the most common relational naming convention.
If Lingua::EN::Inflect is not available on your system, Class::Tables will still work fine, but using a very naive singular-to-plural conversion algorithm ("adding an S at the end").
You can manually disable using Lingua::EN::Inflect with
use Class::Tables inflect => 0;
CAVEATS
Supported database drivers are MySQL, SQLite, and Postgres. With SQLite, you can only get an auto-incremented ID column if it is the first column in the table, and if it is declared as
integer primary key(or you can just use the hiddenrowidcolumn). With Postgres, onlyserial primary keys are supported (usign theoidcolumn as primary key is not yet supported).Pluralization code is only for English at the moment, sorry.
All modifications to objects are instantaneous -- no asynchronous updates and/or rollbacks (yet?)
Only one database handle is used at a time. Calling
Class::Tables->dbha second time will produce undefined results. The parameters used in theuse Class::Tablesline are global.
AUTHOR
Class::Tables is written by Mike Rosulek <mike@mikero.com>. Feel free to contact me with comments, questions, patches, or whatever.
COPYRIGHT
Copyright (c) 2004 Mike Rosulek. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.