NAME

Class::Tables - Relational-object interface with no configuration necessary

SYNOPSIS

The only thing you need to do is give it a database handle to look at, and it Just Works, right out of the box, provided your database follows some very basic rules:

use Class::Tables;
Class::Tables->dbh( DBI->connect($dsn, $user, $passwd) );
## that's all you have to do to get this:

my $new_guy = Employee->new( name => "Bilbo Baggins" );
my $old_guy = Employee->fetch( $id );
my $john    = Employee->search( name => "John Doe" );

$john->name( "Jonathan Doe" );     ## simple accessors/mutators
print $john->age, $/;

print "Stringification to the object's 'name' attribute: $john\n";

my $dept = $john->department;      ## because we also have a table named
print $dept->description, $/;      ## "department", it returns an object

$john->department( $other_dept );  ## assign a Department object
$john->department( 15 );           ##  .. or just the ID of one

my @coworkers = $dept->employee;   ## get all Employee objects that
                                   ## reference this Department
                                   
                                   ## this is also equivalent:
my @coworkers = Employee->search( department => $dept );

DESCRIPTION

The goal of this module is not an all-encompassing object abstraction for relational data. If you want that, see Class::DBI or Alzabo and the like. Instead, Class::Tables aims to be a zero-configuration object abstraction. Using simple rules about the metadata -- the names of tables, columns, and their types -- Class::Tables automatically generates appropriate classes, with object relationships intact. These rules are so simple that you may find you are already following them.

Meta-Data

Primary Key

All tables must have an id column, which is the primary key of the table, and set to AUTO_INCREMENT.

Foreign Key Inflating

A column that shares a name with a table is treated as a foreign key reference to items of that table.

If the employee table has a column called department, and there is a table in the database also named department, then the department accessor for Employee objects will return the object referred to by the ID in that column. The mutator will also accept an appropriate object (or ID).

Conversely, an employee accessor (read-only) would be available to all Department objects that returns all Employee objects referencing the Department object in question.

Lazy Loading

All *blob and *text columns will be lazy-loaded: not queried or stored into memory until requested.

Automatic Sort Order

The first column in the table which is not the id column is the default sort order. All result sets returning multiple objects from a table will be sorted in this order (ascending).

Stringification

If the table has a name column, then its value will be used as the stringification value of an object. Otherwise, the object will stringify to CLASS:ID.

Class Names

Each table must be associated with a package. The default package name for a table in underscore_separated style is the corresponding name translated to StudlyCaps. However, foreign-key accessors are still named according to the column name. So calling $obj->foo_widget returns a FooWidget object.

INTERFACE

Public Interface

Class::Tables->dbh( $dbh )

You must pass Class::Tables an active database handle before you can use any generated object classes.

Data Class Methods

Every class that Class::Tables generates gets the following class methods:

SomeClass->new( [ field => value, ... ] )

Creates a new object in the database with the given values set. If successful, returns the object, otherwise returns undef. You can pass an object or an ID as the value if the field is a foreign key.

SomeClass->search( [ field => value, ... ] )

Searches the appropriate table for objects matching the given restrictions. 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 undef if no objects matched). The scalar context query is slightly optimized. If no arguments are passed to search, every object in the class is returned.

SomeClass->fetch( $id )

Semantically equivalent to SomeClass->search( id => $id ), but slightly optimized internally. Unlike search, will never return multiple items. Returns undef if no object with the given ID exists in the database.

Object Methods

Every object in a Class::Tables-generated class has the following methods:

$obj->delete()

Removes the object from the database.

Accessor/Mutators: $obj->foo( [ $new_val ] )

For each column foo in the table, an accessor/mutator is provided by the same name. It returns the current value of that column for the object. If foo is also the name of another table in the database, then the accessor will return the corresponding Foo object with that ID, or undef if there is no such object. You may pass either a Foo object or an integer ID as the new value,

Alternately, foo can also be the name of a table that has a foreign key pointing to objects of the same type as $obj. If $obj is a Bar object, $obj->foo is exactly equivalent to Foo->search( bar => $obj ).

$obj->field( $field [, $new_val ] )

This is an alternative syntax to accessors/mutators. If you aren't a fan of variable method names, you can use the field method:

for my $thing (qw/name age favorite_color/) {
    ## these two are equivalent:
    print $obj->$thing, $/;
    print $obj->field($thing), $/;
}
$obj->dump

Returns a hashref containing the object's attribute data. Recursively inflates foreign keys, too. Reverse foreign keys are mapped to an array ref. You may find this useful with HTML::Template! Use Data::Dumper on this to see how it does things...

OTHER STUFF

You can still override/augment object methods if you need to with SUPER:

package Employee;
sub ssn {
    my $self = shift;
    my $ssn = $self->SUPER::ssn(@_);
    $ssn =~ s/(\d{3})(\d{2})(\d{4})/$1-$2-$3/;
    return $ssn;
}

But since the objects are blessed scalars, you have to use some sort of inside-out mechanism to store extra (non-persistent) subclass attributes with the objects:

## if you want to do something like this:

for my $emp ( grep { not $_->seen_already } @employees ) {
   ## do something to $emp that you only want to do once..
   ## maybe give $emp a raise?
   
   $emp->see;
}

## in the subclass, you can do this:

package Employee;
my %seen;
sub seen_already { $seen{+shift};   }
sub see          { $seen{+shift}++; }

CAVEATS

So far, the table parsing code only works with MySQL. Same with getting the ID of the last inserted object. Testers/patchers for other DBMS's welcome!

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) 2003 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.