Class-DBI
view release on metacpan or search on metacpan
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');
Then, set up the Film and Actor classes to use this linking table:
Film->table('film');
Film->columns(All => qw/id title rating/);
Film->has_many(stars => [ Role => 'actor' ]);
Actor->table('actor');
Actor->columns(All => qw/id name/);
Actor->has_many(films => [ Role => 'film' ]);
In each case the 'mapping method' variation of has_many() is used to
call the lookup method on the Role object returned. As these methods are
the 'has_a' relationships on the Role, these will return the actual
Actor and Film objects, providing a cheap many-to-many relationship.
In the case of Film, this is equivalent to the more long-winded:
Film->has_many(roles => "Role");
sub actors {
my $self = shift;
return map $_->actor, $self->roles
}
As this is almost exactly what is created internally, add_to_stars and
add_to_films will generally do the right thing as they are actually
( run in 2.042 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )