From: Matt S Trout Date: Mon, 6 Feb 2006 00:46:41 +0000 (+0000) Subject: multi-join to same rel works now X-Git-Tag: v0.05005~47 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=489709af09215594801ecdbe208f542e0432d0a0;p=dbsrgits%2FDBIx-Class.git multi-join to same rel works now --- diff --git a/Changes b/Changes index 3f20f63..639470e 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ Revision history for DBIx::Class 0.05001 + - added multi-join (join => [ 'foo', 'foo' ]), aliases second to foo_2 - hack PK::Auto::Pg for "table" names referencing a schema - find() with attributes works - added experimental Serialize and ResultSetManager components diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 32e4177..799b8d6 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -110,11 +110,10 @@ sub new { push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) unless $seen{$p}; } - my @cols = (); - push @cols, $source->resolve_prefetch($p, $attrs->{alias}); + my @prefetch = $source->resolve_prefetch($p, $attrs->{alias}); #die Dumper \@cols; - push(@{$attrs->{select}}, @cols); - push(@{$attrs->{as}}, @cols); + push(@{$attrs->{select}}, map { $_->[0] } @prefetch); + push(@{$attrs->{as}}, map { $_->[1] } @prefetch); } } diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index ceb5d36..8ea0b1f 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -352,26 +352,31 @@ Returns the join structure required for the related result source =cut sub resolve_join { - my ($self, $join, $alias) = @_; + my ($self, $join, $alias, $seen) = @_; + $seen ||= {}; if (ref $join eq 'ARRAY') { - return map { $self->resolve_join($_, $alias) } @$join; + return map { $self->resolve_join($_, $alias, $seen) } @$join; } elsif (ref $join eq 'HASH') { - return map { $self->resolve_join($_, $alias), - $self->related_source($_)->resolve_join($join->{$_}, $_) } + return + map { $self->resolve_join($_, $alias, $seen), + $self->related_source($_)->resolve_join($join->{$_}, $_, $seen) } keys %$join; } elsif (ref $join) { $self->throw_exception("No idea how to resolve join reftype ".ref $join); } else { + my $count = ++$seen->{$join}; + #use Data::Dumper; warn Dumper($seen); + my $as = ($count > 1 ? "${join}_${count}" : $join); my $rel_info = $self->relationship_info($join); $self->throw_exception("No such relationship ${join}") unless $rel_info; my $type = $rel_info->{attrs}{join_type} || ''; - return [ { $join => $self->related_source($join)->from, + return [ { $as => $self->related_source($join)->from, -join_type => $type }, - $self->resolve_condition($rel_info->{cond}, $join, $alias) ]; + $self->resolve_condition($rel_info->{cond}, $as, $alias) ]; } } -=head2 resolve_condition($cond, $rel, $alias|$object) +=head2 resolve_condition($cond, $as, $alias|$object) Resolves the passed condition to a concrete query fragment. If given an alias, returns a join condition; if given an object, inverts that object to produce @@ -380,7 +385,7 @@ a related conditional from that object. =cut sub resolve_condition { - my ($self, $cond, $rel, $for) = @_; + my ($self, $cond, $as, $for) = @_; #warn %$cond; if (ref $cond eq 'HASH') { my %ret; @@ -393,12 +398,12 @@ sub resolve_condition { $ret{$k} = $for->get_column($v); #warn %ret; } else { - $ret{"${rel}.${k}"} = "${for}.${v}"; + $ret{"${as}.${k}"} = "${for}.${v}"; } } return \%ret; } elsif (ref $cond eq 'ARRAY') { - return [ map { $self->resolve_condition($_, $rel, $for) } @$cond ]; + return [ map { $self->resolve_condition($_, $as, $for) } @$cond ]; } else { die("Can't handle this yet :("); } @@ -448,20 +453,21 @@ in the supplied relationships. Examples: =cut sub resolve_prefetch { - my( $self, $pre, $alias ) = @_; + my ($self, $pre, $alias, $seen) = @_; + $seen ||= {}; use Data::Dumper; #$alias ||= $self->name; #warn $alias, Dumper $pre; if( ref $pre eq 'ARRAY' ) { - return map { $self->resolve_prefetch( $_, $alias ) } @$pre; + return map { $self->resolve_prefetch( $_, $alias, $seen ) } @$pre; } elsif( ref $pre eq 'HASH' ) { my @ret = map { - $self->resolve_prefetch($_, $alias), - $self->related_source($_)->resolve_prefetch( $pre->{$_}, $_ ) - } - keys %$pre; + $self->resolve_prefetch($_, $alias, $seen), + $self->related_source($_)->resolve_prefetch( + $pre->{$_}, "${alias}.$_", $seen) + } keys %$pre; #die Dumper \@ret; return @ret; } @@ -469,12 +475,15 @@ sub resolve_prefetch { $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre); } else { + my $count = ++$seen->{$pre}; + my $as = ($count > 1 ? "${pre}_${count}" : $pre); my $rel_info = $self->relationship_info( $pre ); $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info; - my $prefix = $alias && $alias ne 'me' ? "$alias.$pre" : $pre; - my @ret = map { "$prefix.$_" } $self->related_source($pre)->columns; + my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : ''); + return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } + $self->related_source($pre)->columns; #warn $alias, Dumper (\@ret); - return @ret; + #return @ret; } } diff --git a/t/run/16joins.tl b/t/run/16joins.tl index 3ea71f1..87eaf0a 100644 --- a/t/run/16joins.tl +++ b/t/run/16joins.tl @@ -7,7 +7,7 @@ BEGIN { eval "use DBD::SQLite"; plan $@ ? ( skip_all => 'needs DBD::SQLite for testing' ) - : ( tests => 33 ); + : ( tests => 35 ); } # test the abstract join => SQL generator @@ -225,6 +225,15 @@ $rs = $schema->resultset("CD")->search( cmp_ok( $rs->count, '==', 3, "count() ok after group_by on related column" ); cmp_ok( scalar $rs->all, '==', 3, "all() returns same count as count() after group_by on related column" ); -} + +$rs = $schema->resultset("Artist")->search( + { 'cds.title' => 'Spoonful of bees', + 'cds_2.title' => 'Forkful of bees' }, + { join => [ 'cds', 'cds' ] }); + +cmp_ok($rs->count, '==', 1, "single artist returned from multi-join"); +is($rs->next->name, 'Caterwauler McCrae', "Correct artist returned"); + +} # end run_tests 1;