Class-DBI
view release on metacpan or search on metacpan
- constrain_column can now take subref constraint
- Document DBD::AnyData and FreeTDS issues (Matt Trout)
- Factor out database error handling to _db_error()
- Switch to Class::Accessor::Fast in Column and Relationship
3.0.5 Sep 14 2005
- has_many can take compile time constraints (Cees Hek)
- has_many can take a cascading delete strategy (deprecating the old,
undocumented, 'no_cascade_delete' option)
- columns() can take Class::DBI::Column objects directly
which can now in turn take options, thus allowing things like:
__PACKAGE__->columns(dates => Class::DBI::Column->new(
tdate => { placeholder => 'IF(1, CURDATE(), ?)' }
)
3.0.4 Sep 13 2005
Pre-Reqs
- Note requirement for Scalar::Util 1.08+ (for refaddr)
off caching if required) [gfalck]
- We now warn about column names clashing with any inherited methods,
not just ones defined in Class::DBI itself [Dan Thill]
- Silence some test warnings reported by Paul Makepeace
0.94 Wed Aug 27 2003
New Functionality
- allow has_a() columns to hold NULL values (Dominic Mitchell)
- cascading deletions can be turned off by passing argument to
has_many setup: no_cascade_delete => 1 (undocumented/untested)
[now implemented as a trigger]
- classes and objects can be marked as read_only (experimental and
undocumented, although tested)
Documentation
- no longer refer to Class::DBI::Join, which is dead
- warn about inflating a primary key using has_a
- rewrite of docs on transactions
- numerous small tweaks from Jay Strauss, Alexander Karelas and Brad
Bowman
0.34 Sat Oct 6 2001
- Don't die if a value is a reference. (Ima::DBI does this for us,
and better in case of overloaded objects)
- fix minor problem with mutual hasa / hasa_list referencing
- better diagnostics if hasa_list is miscalled
0.33 Sat Sep 15 2001
+ Added create_filter(), and with it retrieve_all()
+ added docs on how to set-up many-to-many relationships
+ _cascade delete now split out to allow overriding
+ copy() and move() can now take multiple arguments to change
(thanks to Jonathan Swartz)
0.32 Sun Sep 9 2001
+ delete() now removes any foreign elements, to avoid orphans
0.31 Sun Sep 9 2001
+ split out _column_placeholder (thanks to Jonathan Swartz)
+ added hasa() checks for orphaned rows
t/12-filter.t
t/13-constraint.t
t/14-might_have.t
t/15-accessor.t
t/16-reserved.t
t/17-data_type.t
t/18-has_a.t
t/19-set_sql.t
t/21-iterator.t
t/22-deflate_order.t
t/23-cascade.t
t/24-meta_info.t
t/25-closures_in_meta.t
t/26-mutator.t
t/27-mutator-old.t
t/97-pod.t
t/98-failure.t
t/99-misc.t
t/testlib/Actor.pm
t/testlib/Binary.pm
t/testlib/Blurb.pm
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
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;
lib/Class/DBI.pm view on Meta::CPAN
Deletes this object from the database and from memory. If you have set up
any relationships using C<has_many> or C<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 C<before_delete> trigger is when an object instance is about to be
deleted. It is invoked before any cascaded deletes. The C<after_delete>
trigger is invoked after the record has been deleted from the database
and just before the contents in memory are discarded.
=head1 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.
lib/Class/DBI.pm view on Meta::CPAN
sub styles {
my $self = shift;
return map $_->style, $self->_style_refs;
}
For an example of where this is useful see L<"MANY TO MANY RELATIONSHIPS">
below.
=head3 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'
});
=head2 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;
lib/Class/DBI/Cascade/Delete.pm view on Meta::CPAN
This is a Cascading Delete strategy that will delete any related
objects.
=cut
use strict;
use warnings;
use base 'Class::DBI::Cascade::None';
sub cascade {
my ($self, $obj) = @_;
$self->foreign_for($obj)->delete_all;
}
1;
lib/Class/DBI/Cascade/Fail.pm view on Meta::CPAN
package Class::DBI::Cascade::Fail;
=head1 NAME
Class::DBI::Cascade::Fail - Do not cascade if foreign objects exist
=head1 DESCRIPTION
This is a Cascading Delete strategy that will throw an error if any
object about to be deleted still has any other objects pointing at it.
=cut
use strict;
use warnings;
use base 'Class::DBI::Cascade::None';
sub cascade {
my ($self, $obj) = @_;
my $refs = $self->foreign_for($obj)->count or return;
$self->{_rel}->foreign_class->_croak(
"$refs objects still refer to $obj. Deletion failed");
}
1;
lib/Class/DBI/Cascade/None.pm view on Meta::CPAN
=head1 METHODS
=head2 foreign_for
my $iterator = $strategy->foreign_for($obj);
This will return all the objects which are foreign to $obj across the
relationship. It's a normal Class::DBI search you can get the results
either as a list or as an iterator.
=head2 cascade
$strategy->cascade($obj);
Cascade across the related objects to $obj.
=head1 WRITING NEW STRATEGIES
Creating a Cascade strategy should be fairly simple. You usually just
need to inherit from here, and then supply a cascade() method that does
the required thing with the results from foreign_for().
So, for example, Cascade::Delete is implemented simply as:
package Class::DBI::Cascade::Delete;
use base 'Class::DBI::Cascade::None';
sub cascade {
my ($self, $obj) = @_;
$self->foreign_for($obj)->delete_all;
}
=cut
use strict;
use warnings;
sub new {
my ($class, $rel) = @_;
bless { _rel => $rel } => $class;
}
sub foreign_for {
my ($self, $obj) = @_;
return $self->{_rel}
->foreign_class->search($self->{_rel}->args->{foreign_key} => $obj->id);
}
sub cascade { return; }
1;
lib/Class/DBI/Relationship/HasMany.pm view on Meta::CPAN
sub _set_up_class_data {
my $self = shift;
$self->class->_extend_class_data(
__hasa_list => $self->foreign_class => $self->args->{foreign_key});
$self->SUPER::_set_up_class_data;
}
sub triggers {
my $self = shift;
if ($self->args->{no_cascade_delete}) { # old undocumented way
warn "no_cascade_delete deprecated in favour of cascade => None";
return;
}
my $strategy = $self->args->{cascade} || "Delete";
$strategy = "Class::DBI::Cascade::$strategy" unless $strategy =~ /::/;
$self->foreign_class->_require_class($strategy);
$strategy->can('cascade')
or return $self->_croak("$strategy is not a valid Cascade Strategy");
my $strat_obj = $strategy->new($self);
return (before_delete => sub { $strat_obj->cascade(@_) });
}
sub methods {
my $self = shift;
my $accessor = $self->accessor;
return (
$accessor => $self->_has_many_method,
"add_to_$accessor" => $self->_method_add_to,
);
}
t/10-mysql.t view on Meta::CPAN
ok MyStar->has_many(filmids => [ MyStarLink => 'film', 'id' ]),
"**** Multi-map";
my @filmid = $s1->filmids;
ok !ref $filmid[0], "Film-id is not a reference";
my $first = $s1->filmids->first;
ok !ref $first, "First is not a reference";
is $first, $filmid[0], "But it's the same as filmid[0]";
}
{ # cascades correctly
my $lenin = MyFilm->insert({ title => "Leningrad Cowboys Go America" });
my $pimme = MyStar->insert({ name => "Pimme Korhonen" });
my $cowboy = MyStarLink->insert({ film => $lenin, star => $pimme });
$lenin->delete;
is MyStar->search(name => 'Pimme Korhonen')->count, 1, "Pimme still exists";
is MyStarLink->search(star => $pimme->id)->count, 0, "But in no films";
}
{
ok MyStar->has_many(filmids_mcpk => [ MyStarLinkMCPK => 'film', 'id' ]),
t/14-might_have.t view on Meta::CPAN
is $bt->blurb, $info->blurb, "Blurb is the same as fetching the long way";
ok $bt->blurb("New blurb"), "We can set the blurb";
$bt->update;
is $bt->blurb, $info->blurb, "Blurb has been set";
$bt->rating(18);
eval { $bt->update };
is $@, '', "No problems updating when do have";
is $bt->rating, 18, "Updated OK";
# cascade delete?
{
my $blurb = Blurb->retrieve('Bad Taste');
isa_ok $blurb => "Blurb";
$bt->delete;
$blurb = Blurb->retrieve('Bad Taste');
is $blurb, undef, "Blurb has gone";
}
}
t/23-cascade.t view on Meta::CPAN
BEGIN {
eval "use DBD::SQLite";
plan $@ ? (skip_all => 'needs DBD::SQLite for testing') : (tests => 5);
}
use lib 't/testlib';
use Film;
use Director;
{ # Cascade Strategies
Director->has_many(nasties => Film => { cascade => 'Fail' });
my $dir = Director->insert({ name => "Nasty Noddy" });
my $kk = $dir->add_to_nasties({ Title => 'Killer Killers' });
is $kk->director, $dir, "Director set OK";
is $dir->nasties, 1, "We have one nasty";
eval { $dir->delete };
like $@, qr/1/, "Can't delete while films exist";
my $rr = $dir->add_to_nasties({ Title => 'Revenge of the Revengers' });
eval { $dir->delete };
like $@, qr/2/, "Still can't delete";
( run in 0.576 second using v1.01-cache-2.11-cpan-49f99fa48dc )