Constructor
AccessorMapping
ColumnCase
- MightHave
HasMany
HasA
LazyLoading
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;
}
($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';
+++ /dev/null
-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;
my $message = $params{message} || $params{error} || $! || '';
- local $Carp::CarpLevel = 1;
+ local $Carp::CarpLevel = (caller(1) eq 'NEXT' ? 2 : 1);
Carp::croak($message);
}
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', { } );
--- /dev/null
+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;
--- /dev/null
+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;
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;
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;
# 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