From: Matt S Trout Date: Sun, 18 Sep 2005 01:53:34 +0000 (+0000) Subject: Addition of a bunch of helper relationship methods X-Git-Tag: v0.03001~8 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=07037f89d4d9bf97c59a2c083de74f669521da47;p=dbsrgits%2FDBIx-Class.git Addition of a bunch of helper relationship methods --- diff --git a/lib/DBIx/Class/CDBICompat.pm b/lib/DBIx/Class/CDBICompat.pm index c221a61..daf5822 100644 --- a/lib/DBIx/Class/CDBICompat.pm +++ b/lib/DBIx/Class/CDBICompat.pm @@ -16,7 +16,6 @@ __PACKAGE__->load_own_components(qw/ Constructor AccessorMapping ColumnCase - MightHave HasMany HasA LazyLoading diff --git a/lib/DBIx/Class/CDBICompat/HasA.pm b/lib/DBIx/Class/CDBICompat/HasA.pm index b635429..3010954 100644 --- a/lib/DBIx/Class/CDBICompat/HasA.pm +++ b/lib/DBIx/Class/CDBICompat/HasA.pm @@ -20,10 +20,10 @@ sub has_a { return 1; } my ($pri, $too_many) = keys %{ $f_class->_primaries }; - $self->throw( "has_a only works with a single primary key; ${f_class} has more. try using a has_one relationship instead of Class::DBI compat rels" ) + $self->throw( "has_a only works with a single primary key; ${f_class} has more. try using a belongs_to relationship instead of Class::DBI compat rels" ) if $too_many; - $self->has_one($col, $f_class); + $self->belongs_to($col, $f_class); return 1; } diff --git a/lib/DBIx/Class/CDBICompat/HasMany.pm b/lib/DBIx/Class/CDBICompat/HasMany.pm index acc7041..3f745f6 100644 --- a/lib/DBIx/Class/CDBICompat/HasMany.pm +++ b/lib/DBIx/Class/CDBICompat/HasMany.pm @@ -12,40 +12,15 @@ sub has_many { ($f_class, @f_method) = @$f_class; } - my ($pri, $too_many) = keys %{ $class->_primaries }; - $class->throw( "has_many only works with a single primary key; ${class} has more" ) - if $too_many; - my $self_key = $pri; - - eval "require $f_class"; + if (ref $f_key eq 'HASH' && !$args) { $args = $f_key; undef $f_key; }; - if (ref $f_key eq 'HASH') { $args = $f_key; undef $f_key; }; - - #unless ($f_key) { Not selective enough. Removed pending fix. - # ($f_rel) = grep { $_->{class} && $_->{class} eq $class } - # $f_class->_relationships; - #} - - unless ($f_key) { - #warn join(', ', %{ $f_class->_columns }); - $class =~ /([^\:]+)$/; - #warn $1; - $f_key = lc $1 if $f_class->_columns->{lc $1}; + $args ||= {}; + if (delete $args->{no_cascade_delete}) { + $args->{cascade_delete} = 0; } - $class->throw( "Unable to resolve foreign key for has_many from ${class} to ${f_class}" ) - unless $f_key; - $class->throw( "No such column ${f_key} on foreign class ${f_class}" ) - unless $f_class->_columns->{$f_key}; - $args ||= {}; - my $cascade = not (ref $args eq 'HASH' && delete $args->{no_cascade_delete}); + $class->NEXT::has_many($rel, $f_class, $f_key, $args); - $class->add_relationship($rel, $f_class, - { "foreign.${f_key}" => "self.${self_key}" }, - { accessor => 'multi', - join_type => 'LEFT', - ($cascade ? ('cascade_delete' => 1) : ()), - %$args } ); if (@f_method) { no strict 'refs'; no warnings 'redefine'; diff --git a/lib/DBIx/Class/CDBICompat/MightHave.pm b/lib/DBIx/Class/CDBICompat/MightHave.pm deleted file mode 100644 index 5cf073e..0000000 --- a/lib/DBIx/Class/CDBICompat/MightHave.pm +++ /dev/null @@ -1,22 +0,0 @@ -package DBIx::Class::CDBICompat::MightHave; - -use strict; -use warnings; - -sub might_have { - my ($class, $rel, $f_class, @columns) = @_; - my ($pri, $too_many) = keys %{ $class->_primaries }; - $class->throw( "might_have only works with a single primary key; ${class} has more" ) - if $too_many; - my $f_pri; - ($f_pri, $too_many) = keys %{ $f_class->_primaries }; - $class->throw( "might_have only works with a single primary key; ${f_class} has more" ) - if $too_many; - $class->add_relationship($rel, $f_class, - { "foreign.${f_pri}" => "self.${pri}" }, - { accessor => 'single', proxy => \@columns, - cascade_update => 1, cascade_delete => 1 }); - 1; -} - -1; diff --git a/lib/DBIx/Class/Exception.pm b/lib/DBIx/Class/Exception.pm index cfa7887..40426ad 100644 --- a/lib/DBIx/Class/Exception.pm +++ b/lib/DBIx/Class/Exception.pm @@ -49,7 +49,7 @@ sub throw { my $message = $params{message} || $params{error} || $! || ''; - local $Carp::CarpLevel = 1; + local $Carp::CarpLevel = (caller(1) eq 'NEXT' ? 2 : 1); Carp::croak($message); } diff --git a/lib/DBIx/Class/Relationship.pm b/lib/DBIx/Class/Relationship.pm index 4b9d64f..d7d1675 100644 --- a/lib/DBIx/Class/Relationship.pm +++ b/lib/DBIx/Class/Relationship.pm @@ -5,7 +5,15 @@ use warnings; use base qw/DBIx::Class Class::Data::Inheritable/; -__PACKAGE__->load_own_components(qw/Accessor CascadeActions ProxyMethods Base HasOne/); +__PACKAGE__->load_own_components(qw/ + HasMany + HasOne + BelongsTo + Accessor + CascadeActions + ProxyMethods + Base +/); __PACKAGE__->mk_classdata('_relationships', { } ); diff --git a/lib/DBIx/Class/Relationship/BelongsTo.pm b/lib/DBIx/Class/Relationship/BelongsTo.pm new file mode 100644 index 0000000..4acb8fe --- /dev/null +++ b/lib/DBIx/Class/Relationship/BelongsTo.pm @@ -0,0 +1,42 @@ +package DBIx::Class::Relationship::BelongsTo; + +use strict; +use warnings; + +sub belongs_to { + my ($class, $rel, $f_class, $cond, $attrs) = @_; + eval "require $f_class"; + # single key relationship + if (not defined $cond) { + my ($pri, $too_many) = keys %{ $f_class->_primaries }; + my $acc_type = ($class->_columns->{$rel}) ? 'filter' : 'single'; + $class->add_relationship($rel, $f_class, + { "foreign.${pri}" => "self.${rel}" }, + { accessor => $acc_type } + ); + } + # multiple key relationship + else { + my %f_primaries = %{ $f_class->_primaries }; + my $cond_rel; + for (keys %$cond) { + $cond_rel->{"foreign.$_"} = "self.".$cond->{$_}; + # primary key usage checks + if (exists $f_primaries{$_}) { + delete $f_primaries{$_}; + } + else + { + $class->throw("non primary key used in join condition: $_"); + } + } + $class->throw("not all primary keys used in multi key relationship!") if keys %f_primaries; + $class->add_relationship($rel, $f_class, + $cond_rel, + { accessor => 'single', %{$attrs ||{}} } + ); + } + return 1; +} + +1; diff --git a/lib/DBIx/Class/Relationship/HasMany.pm b/lib/DBIx/Class/Relationship/HasMany.pm new file mode 100644 index 0000000..2efebb7 --- /dev/null +++ b/lib/DBIx/Class/Relationship/HasMany.pm @@ -0,0 +1,36 @@ +package DBIx::Class::Relationship::HasMany; + +use strict; +use warnings; + +sub has_many { + my ($class, $rel, $f_class, $cond, $attrs) = @_; + + eval "require $f_class"; + + if (!ref $cond) { + my $f_key; + if (defined $cond && length $cond) { + $f_key = $cond; + $class->throw( "No such column ${f_key} on foreign class ${f_class}" ) + unless ($@ || $f_class->_columns->{$f_key}); + } else { + $class =~ /([^\:]+)$/; + $f_key = lc $1 if $f_class->_columns->{lc $1}; + $class->throw( "Unable to resolve foreign key for has_many from ${class} to ${f_class}" ) + unless $f_key; + } + my ($pri, $too_many) = keys %{ $class->_primaries }; + $class->throw( "has_many can only infer join for a single primary key; ${class} has more" ) + if $too_many; + $cond = { "foreign.${f_key}" => "self.${pri}" }, + } + + $class->add_relationship($rel, $f_class, $cond, + { accessor => 'multi', + join_type => 'LEFT', + cascade_delete => 1, + %{$attrs||{}} } ); +} + +1; diff --git a/lib/DBIx/Class/Relationship/HasOne.pm b/lib/DBIx/Class/Relationship/HasOne.pm index 6128f7a..ea97e7d 100644 --- a/lib/DBIx/Class/Relationship/HasOne.pm +++ b/lib/DBIx/Class/Relationship/HasOne.pm @@ -3,40 +3,46 @@ package DBIx::Class::Relationship::HasOne; use strict; use warnings; +sub might_have { + shift->_has_one('LEFT' => @_); +} + sub has_one { - my ($class, $acc_name, $f_class, $cond) = @_; - eval "require $f_class"; - # single key relationship - if (not defined $cond) { - my ($pri, $too_many) = keys %{ $f_class->_primaries }; - my $acc_type = ($class->_columns->{$acc_name}) ? 'filter' : 'single'; - $class->add_relationship($acc_name, $f_class, - { "foreign.${pri}" => "self.${acc_name}" }, - { accessor => $acc_type } - ); - } - # multiple key relationship - else { - my %f_primaries = %{ $f_class->_primaries }; - my $cond_rel; - for (keys %$cond) { - $cond_rel->{"foreign.$_"} = "self.".$cond->{$_}; - # primary key usage checks - if (exists $f_primaries{$_}) { - delete $f_primaries{$_}; - } - else - { - $class->throw("non primary key used in join condition: $_"); - } + shift->_has_one(undef => @_); +} + +sub _has_one { + my ($class, $join_type, $rel, $f_class, @columns) = @_; + my $cond; + if (ref $columns[0]) { + $cond = shift @columns; + } else { + my ($pri, $too_many) = keys %{ $class->_primaries }; + $class->throw( "might_have/has_one can only infer join for a single primary key; ${class} has more" ) + if $too_many; + my $f_key; + if ($f_class->_columns->{$rel}) { + $f_key = $rel; + } else { + ($f_key, $too_many) = keys %{ $f_class->_primaries }; + $class->throw( "might_have/has_one can only infer join for a single primary key; ${f_class} has more" ) + if $too_many; } - $class->throw("not all primary keys used in multi key relationship!") if keys %f_primaries; - $class->add_relationship($acc_name, $f_class, - $cond_rel, - { accessor => 'single' } - ); + $cond = { "foreign.${f_key}" => "self.${pri}" }, + } + shift(@columns) unless defined $columns[0]; # Explicit empty condition + my %attrs; + if (ref $columns[0] eq 'HASH') { + %attrs = %{shift @columns}; } - return 1; + shift(@columns) unless defined $columns[0]; # Explicit empty attrs + $class->add_relationship($rel, $f_class, + $cond, + { accessor => 'single', (@columns ? (proxy => \@columns) : ()), + cascade_update => 1, cascade_delete => 1, + ($join_type ? ('join_type' => $join_type) : ()), + %attrs }); + 1; } 1; diff --git a/t/lib/DBICTest/Schema/HelperRels.pm b/t/lib/DBICTest/Schema/HelperRels.pm index 4c74bc4..590c99f 100644 --- a/t/lib/DBICTest/Schema/HelperRels.pm +++ b/t/lib/DBICTest/Schema/HelperRels.pm @@ -2,79 +2,33 @@ package DBICTest::Schema::BasicRels; use base 'DBIx::Class::Core'; -DBICTest::Schema::Artist->add_relationship( - cds => 'DBICTest::Schema::CD', - { 'foreign.artist' => 'self.artistid' }, - { order_by => 'year' } -); -DBICTest::Schema::Artist->add_relationship( - twokeys => 'DBICTest::Schema::TwoKeys', - { 'foreign.artist' => 'self.artistid' } -); -DBICTest::Schema::Artist->add_relationship( - onekeys => 'DBICTest::Schema::OneKey', - { 'foreign.artist' => 'self.artistid' } -); +DBICTest::Schema::Artist->has_many(cds => 'DBICTest::Schema::CD', undef, + { order_by => 'year' }); +DBICTest::Schema::Artist->has_many(twokeys => 'DBICTest::Schema::TwoKeys'); +DBICTest::Schema::Artist->has_many(onekeys => 'DBICTest::Schema::OneKey'); -DBICTest::Schema::CD->has_one('artist', 'DBICTest::Schema::Artist'); -#DBICTest::Schema::CD->add_relationship( -# artist => 'DBICTest::Schema::Artist', -# { 'foreign.artistid' => 'self.artist' }, -#); -DBICTest::Schema::CD->add_relationship( - tracks => 'DBICTest::Schema::Track', - { 'foreign.cd' => 'self.cdid' } -); -DBICTest::Schema::CD->add_relationship( - tags => 'DBICTest::Schema::Tag', - { 'foreign.cd' => 'self.cdid' } -); -#DBICTest::Schema::CD->might_have(liner_notes => 'DBICTest::Schema::LinerNotes' => qw/notes/); -DBICTest::Schema::CD->add_relationship( - liner_notes => 'DBICTest::Schema::LinerNotes', - { 'foreign.liner_id' => 'self.cdid' }, - { join_type => 'LEFT' } -); +DBICTest::Schema::CD->belongs_to('artist', 'DBICTest::Schema::Artist'); -DBICTest::Schema::SelfRefAlias->add_relationship( - self_ref => 'DBICTest::Schema::SelfRef', - { 'foreign.id' => 'self.self_ref' }, - { accessor => 'single' } +DBICTest::Schema::CD->has_many(tracks => 'DBICTest::Schema::Track'); +DBICTest::Schema::CD->has_many(tags => 'DBICTest::Schema::Tag'); -); -DBICTest::Schema::SelfRefAlias->add_relationship( - alias => 'DBICTest::Schema::SelfRef', - { 'foreign.id' => 'self.alias' }, - { accessor => 'single' } -); +DBICTest::Schema::CD->might_have(liner_notes => 'DBICTest::Schema::LinerNotes' => qw/notes/); -DBICTest::Schema::SelfRef->add_relationship( - aliases => 'DBICTest::Schema::SelfRefAlias', - { 'foreign.self_ref' => 'self.id' }, - { accessor => 'multi' } -); +DBICTest::Schema::SelfRefAlias->belongs_to( + self_ref => 'DBICTest::Schema::SelfRef'); -DBICTest::Schema::Tag->has_one('cd', 'DBICTest::Schema::CD'); -#DBICTest::Schema::Tag->add_relationship( -# cd => 'DBICTest::Schema::CD', -# { 'foreign.cdid' => 'self.cd' } -#); +DBICTest::Schema::SelfRefAlias->belongs_to( + alias => 'DBICTest::Schema::SelfRef'); -DBICTest::Schema::Track->has_one('cd', 'DBICTest::Schema::CD'); -#DBICTest::Schema::Track->add_relationship( -# cd => 'DBICTest::Schema::CD', -# { 'foreign.cdid' => 'self.cd' } -#); +DBICTest::Schema::SelfRef->has_many( + aliases => 'DBICTest::Schema::SelfRefAlias' => 'self_ref'); -DBICTest::Schema::TwoKeys->has_one('artist', 'DBICTest::Schema::Artist'); -# DBICTest::Schema::TwoKeys->add_relationship( -# artist => 'DBICTest::Schema::Artist', -# { 'foreign.artistid' => 'self.artist' } -# ); -DBICTest::Schema::TwoKeys->has_one('cd', 'DBICTest::Schema::CD'); -#DBICTest::Schema::TwoKeys->add_relationship( -# cd => 'DBICTest::Schema::CD', -# { 'foreign.cdid' => 'self.cd' } -#); +DBICTest::Schema::Tag->belongs_to('cd', 'DBICTest::Schema::CD'); + +DBICTest::Schema::Track->belongs_to('cd', 'DBICTest::Schema::CD'); + +DBICTest::Schema::TwoKeys->belongs_to('artist', 'DBICTest::Schema::Artist'); + +DBICTest::Schema::TwoKeys->belongs_to('cd', 'DBICTest::Schema::CD'); 1; diff --git a/t/run/06relationship.tl b/t/run/06relationship.tl index 93f47c4..04d2249 100644 --- a/t/run/06relationship.tl +++ b/t/run/06relationship.tl @@ -4,22 +4,35 @@ plan tests => 13; # has_a test my $cd = DBICTest::CD->find(4); -my ($artist) = $cd->search_related('artist'); +my ($artist) = ($INC{'DBICTest/HelperRels'} + ? $cd->artist + : $cd->search_related('artist')); is($artist->name, 'Random Boy Band', 'has_a search_related ok'); # has_many test with an order_by clause defined $artist = DBICTest::Artist->find(1); -is( ($artist->search_related('cds'))[1]->title, 'Spoonful of bees', 'has_many search_related with order_by ok' ); +my @cds = ($INC{'DBICTest/HelperRels'} + ? $artist->cds + : $artist->search_related('cds')); +is( $cds[1]->title, 'Spoonful of bees', 'has_many search_related with order_by ok' ); # search_related with additional abstract query -my @cds = $artist->search_related('cds', { title => { like => '%of%' } } ); +@cds = ($INC{'DBICTest/HelperRels'} + ? $artist->cds({ title => { like => '%of%' } }) + : $artist->search_related('cds', { title => { like => '%of%' } } ) + ); is( $cds[1]->title, 'Forkful of bees', 'search_related with abstract query ok' ); # creating a related object -$artist->create_related( 'cds', { - title => 'Big Flop', - year => 2005, -} ); +if ($INC{'DBICTest/HelperRels.pm'}) { + $artist->add_to_cds({ title => 'Big Flop', year => 2005 }); +} else { + $artist->create_related( 'cds', { + title => 'Big Flop', + year => 2005, + } ); +} + is( ($artist->search_related('cds'))[3]->title, 'Big Flop', 'create_related ok' ); # count_related