Class-DBI
view release on metacpan or search on metacpan
%data consists of the initial information to place in your object and
the database. The keys of %data match up with the columns of your
objects and the values are the initial settings of those fields.
my $cd = Music::CD->insert({
cdid => 1,
artist => $artist,
title => 'October',
year => 1980,
});
If the table has a single primary key column and that column value is
not defined in %data, insert() will assume it is to be generated. If a
sequence() has been specified for this Class, it will use that.
Otherwise, it will assume the primary key can be generated by
AUTO_INCREMENT and attempt to use that.
The "before_create" trigger is invoked directly after storing the
supplied values into the new object and before inserting the record into
the database. The object stored in $self may not have all the
functionality of the final object after_creation, particularly if the
database is going to be providing the primary key value.
For tables with multi-column primary keys you need to supply all the key
values, either in the arguments to the insert() method, or by setting
the values in a "before_create" trigger.
If the class has declared relationships with foreign classes via
has_a(), you can pass an object to insert() for the value of that key.
Class::DBI will Do The Right Thing.
After the new record has been inserted into the database the data for
non-primary key columns is discarded from the object. If those columns
are accessed again they'll simply be fetched as needed. This ensures
that the data in the application is consistent with what the database
*actually* stored.
The "after_create" trigger is invoked after the database insert has
executed.
find_or_create
my $cd = Music::CD->find_or_create({ artist => 'U2', title => 'Boy' });
This checks if a CD can be found to match the information passed, and if
not inserts it.
delete
$obj->delete;
Music::CD->search(year => 1980, title => 'Greatest %')->delete_all;
Deletes this object from the database and from memory. If you have set
up any relationships using "has_many" or "might_have", this will delete
the foreign elements also, recursively (cascading delete). $obj is no
longer usable after this call.
Multiple objects can be deleted by calling delete_all on the Iterator
returned from a search. Each object found will be deleted in turn, so
cascading delete and other triggers will be honoured.
The "before_delete" trigger is when an object instance is about to be
deleted. It is invoked before any cascaded deletes. The "after_delete"
trigger is invoked after the record has been deleted from the database
and just before the contents in memory are discarded.
RETRIEVING OBJECTS
Class::DBI provides a few very simple search methods.
It is not the goal of Class::DBI to replace the need for using SQL.
Users are expected to write their own searches for more complex cases.
Class::DBI::AbstractSearch, available on CPAN, provides a much more
complex search interface than Class::DBI provides itself.
retrieve
$obj = Class->retrieve( $id );
$obj = Class->retrieve( %key_values );
Given key values it will retrieve the object with that key from the
database. For tables with a single column primary key a single parameter
can be used, otherwise a hash of key-name key-value pairs must be given.
my $cd = Music::CD->retrieve(1) or die "No such cd";
retrieve_all
my @objs = Class->retrieve_all;
my $iterator = Class->retrieve_all;
Retrieves objects for all rows in the database. This is probably a bad
idea if your table is big, unless you use the iterator version.
search
@objs = Class->search(column1 => $value, column2 => $value ...);
This is a simple search for all objects where the columns specified are
equal to the values specified e.g.:
@cds = Music::CD->search(year => 1990);
@cds = Music::CD->search(title => "Greatest Hits", year => 1990);
You may also specify the sort order of the results by adding a final
hash of arguments with the key 'order_by':
@cds = Music::CD->search(year => 1990, { order_by=>'artist' });
This is passed through 'as is', enabling order_by clauses such as 'year
DESC, title'.
search_like
@objs = Class->search_like(column1 => $like_pattern, ....);
This is a simple search for all objects where the columns specified are
like the values specified. $like_pattern is a pattern given in SQL LIKE
predicate syntax. '%' means "any zero or more characters", '_' means
"any single character".
@cds = Music::CD->search_like(title => 'October%');
@cds = Music::CD->search_like(title => 'Hits%', artist => 'Various%');
You can also use 'order_by' with these, as with search().
ITERATORS
my $track6 = Music::Track->insert({
cd => $cd,
position => 6,
title => 'Tomorrow',
});
When setting up the relationship the foreign class's has_a()
declarations are examined to discover which of its columns reference our
class. (Note that because this happens at compile time, if the foreign
class is defined in the same file, the class with the has_a() must be
defined earlier than the class with the has_many(). If the classes are
in different files, Class::DBI should usually be able to do the right
things, as long as all classes inherit Class::DBI before 'use'ing any
other classes.)
If the foreign class has no has_a() declarations linking to this class,
it is assumed that the foreign key in that class is named after the
moniker() of this class.
If this is not true you can pass an additional third argument to the
has_many() declaration stating which column of the foreign class is the
foreign key to this class.
Limiting
Music::Artist->has_many(cds => 'Music::CD');
my @cds = $artist->cds(year => 1980);
When calling the method created by has_many, you can also supply any
additional key/value pairs for restricting the search. The above example
will only return the CDs with a year of 1980.
Ordering
Music::CD->has_many(tracks => 'Music::Track', { order_by => 'playorder' });
has_many takes an optional final hashref of options. If an 'order_by'
option is set, its value will be set in an ORDER BY clause in the SQL
issued. This is passed through 'as is', enabling order_by clauses such
as 'length DESC, position'.
Mapping
Music::CD->has_many(styles => [ 'Music::StyleRef' => 'style' ]);
If the second argument to has_many is turned into a listref of the
Classname and an additional method, then that method will be called in
turn on each of the objects being returned.
The above is exactly equivalent to:
Music::CD->has_many(_style_refs => 'Music::StyleRef');
sub styles {
my $self = shift;
return map $_->style, $self->_style_refs;
}
For an example of where this is useful see "MANY TO MANY RELATIONSHIPS"
below.
Cascading Delete
Music::Artist->has_many(cds => 'Music::CD', { cascade => 'Fail' });
It is also possible to control what happens to the 'child' objects when
the 'parent' object is deleted. By default this is set to 'Delete' - so,
for example, when you delete an artist, you also delete all their CDs,
leaving no orphaned records. However you could also set this to 'None',
which would leave all those orphaned records (although this generally
isn't a good idea), or 'Fail', which will throw an exception when you
try to delete an artist that still has any CDs.
You can also write your own Cascade strategies by supplying a Class Name
here.
For example you could write a Class::DBI::Cascade::Plugin::Nullify which
would set all related foreign keys to be NULL, and plug it into your
relationship:
Music::Artist->has_many(cds => 'Music::CD', {
cascade => 'Class::DBI::Cascade::Plugin::Nullify'
});
might_have
Music::CD->might_have(method_name => Class => (@fields_to_import));
Music::CD->might_have(liner_notes => LinerNotes => qw/notes/);
my $liner_notes_object = $cd->liner_notes;
my $notes = $cd->notes; # equivalent to $cd->liner_notes->notes;
might_have() is similar to has_many() for relationships that can have at
most one associated objects. For example, if you have a CD database to
which you want to add liner notes information, you might not want to add
a 'liner_notes' column to your main CD table even though there is no
multiplicity of relationship involved (each CD has at most one 'liner
notes' field). So, you create another table with the same primary key as
this one, with which you can cross-reference.
But you don't want to have to keep writing methods to turn the the
'list' of liner_notes objects you'd get back from has_many into the
single object you'd need. So, might_have() does this work for you. It
creates an accessor to fetch the single object back if it exists, and it
also allows you import any of its methods into your namespace. So, in
the example above, the LinerNotes class can be mostly invisible - you
can just call $cd->notes and it will call the notes method on the
correct LinerNotes object transparently for you.
Making sure you don't have namespace clashes is up to you, as is
correctly creating the objects, but this may be made simpler in later
versions. (Particularly if someone asks for this!)
Notes
has_a(), might_have() and has_many() check that the relevant class has
already been loaded. If it hasn't then they try to load the module of
the same name using require. If the require fails because it can't find
the module then it will assume it's not a simple require (i.e.,
Foreign::Class isn't in Foreign/Class.pm) and that you will take care of
it and ignore the warning. Any other error, such as a syntax error,
triggers an exception.
NOTE: The two classes in a relationship do not have to be in the same
database, on the same machine, or even in the same type of database! It
is quite acceptable for a table in a MySQL database to be connected to a
different table in an Oracle database, and for cascading delete etc to
work across these. This should assist greatly if you need to migrate a
database gradually.
MANY TO MANY RELATIONSHIPS
Class::DBI does not currently support Many to Many relationships, per
se. However, by combining the relationships that already exist it is
possible to set these up.
Consider the case of Films and Actors, with a linking Role table with a
multi-column Primary Key. First of all set up the Role class:
Role->table('role');
Role->columns(Primary => qw/film actor/);
Role->has_a(film => 'Film');
Role->has_a(actor => 'Actor');
( run in 0.721 second using v1.01-cache-2.11-cpan-39bf76dae61 )