X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=f72dc6663f2e0830dfa2bee236687eceb8805945;hb=bd93520f2fc007efd8d0062f44b611cb00072b7d;hp=d6d50cf9a019599d65bbcc1aeb647e5bb123c270;hpb=08a39415df06d59dde0e545bac5c7dd46a8f4a03;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index d6d50cf..f72dc66 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -11,7 +11,7 @@ use Data::Page; use Storable; use Data::Dumper; use Scalar::Util qw/weaken/; - +use Data::Dumper; use DBIx::Class::ResultSetColumn; use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/AccessorGroup/); @@ -160,14 +160,29 @@ always return a resultset, even in list context. sub search_rs { my $self = shift; - my $our_attrs = { %{$self->{attrs}} }; - my $having = delete $our_attrs->{having}; my $attrs = {}; $attrs = pop(@_) if @_ > 1 and ref $_[$#_] eq 'HASH'; - + my $our_attrs = ($attrs->{_parent_attrs}) ? { %{$attrs->{_parent_attrs}} } : { %{$self->{attrs}} }; + my $having = delete $our_attrs->{having}; + + # XXX this is getting messy + if ($attrs->{_live_join_stack}) { + my $live_join = $attrs->{_live_join_stack}; + foreach (reverse @{$live_join}) { + $attrs->{_live_join_h} = (defined $attrs->{_live_join_h}) ? { $_ => $attrs->{_live_join_h} } : $_; + } + } + # merge new attrs into old foreach my $key (qw/join prefetch/) { next unless (exists $attrs->{$key}); + if ($attrs->{_live_join_stack} || $our_attrs->{_live_join_stack}) { + my $live_join = $attrs->{_live_join_stack} || $our_attrs->{_live_join_stack}; + foreach (reverse @{$live_join}) { + $attrs->{$key} = { $_ => $attrs->{$key} }; + } + } + if (exists $our_attrs->{$key}) { $our_attrs->{$key} = $self->_merge_attr($our_attrs->{$key}, $attrs->{$key}); } else { @@ -176,13 +191,13 @@ sub search_rs { delete $attrs->{$key}; } + $our_attrs->{join} = $self->_merge_attr($our_attrs->{join}, $attrs->{_live_join_h}, 1) if ($attrs->{_live_join_h}); + if (exists $our_attrs->{prefetch}) { $our_attrs->{join} = $self->_merge_attr($our_attrs->{join}, $our_attrs->{prefetch}, 1); } my $new_attrs = { %{$our_attrs}, %{$attrs} }; - - # merge new where and having into old my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift @@ -464,7 +479,7 @@ sub single { } unless ($self->_is_unique_query($attrs->{where})) { - carp "Query not guarnteed to return a single row" + carp "Query not guaranteed to return a single row" . "; please declare your unique constraints or use search instead"; } @@ -670,7 +685,7 @@ sub _resolve { return if(exists $self->{_attrs}); #return if _resolve has already been called - my $attrs = $self->{attrs}; + my $attrs = $self->{attrs}; my $source = ($self->{_parent_rs}) ? $self->{_parent_rs} : $self->{result_source}; # XXX - lose storable dclone @@ -700,33 +715,32 @@ sub _resolve { $attrs->{seen_join} ||= {}; my %seen; if (my $join = delete $attrs->{join}) { - foreach my $j (ref $join eq 'ARRAY' ? @$join : ($join)) { - if (ref $j eq 'HASH') { - $seen{$_} = 1 foreach keys %$j; - } else { - $seen{$j} = 1; - } - } - - push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}, $attrs->{seen_join})); + foreach my $j (ref $join eq 'ARRAY' ? @$join : ($join)) { + if (ref $j eq 'HASH') { + $seen{$_} = 1 foreach keys %$j; + } else { + $seen{$j} = 1; + } + } + + push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}, $attrs->{seen_join})); } $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct}; $attrs->{order_by} = [ $attrs->{order_by} ] if $attrs->{order_by} and !ref($attrs->{order_by}); $attrs->{order_by} ||= []; - if(my $seladds = delete($attrs->{'+select'})) { - my @seladds = (ref($seladds) eq 'ARRAY' ? @$seladds : ($seladds)); - $attrs->{select} = [ - @{ $attrs->{select} }, - map { (m/\./ || ref($_)) ? $_ : "${alias}.$_" } $seladds - ]; - } - if(my $asadds = delete($attrs->{'+as'})) { - my @asadds = (ref($asadds) eq 'ARRAY' ? @$asadds : ($asadds)); - $attrs->{as} = [ @{ $attrs->{as} }, @asadds ]; - } - + if(my $seladds = delete($attrs->{'+select'})) { + my @seladds = (ref($seladds) eq 'ARRAY' ? @$seladds : ($seladds)); + $attrs->{select} = [ + @{ $attrs->{select} }, + map { (m/\./ || ref($_)) ? $_ : "${alias}.$_" } $seladds + ]; + } + if(my $asadds = delete($attrs->{'+as'})) { + my @asadds = (ref($asadds) eq 'ARRAY' ? @$asadds : ($asadds)); + $attrs->{as} = [ @{ $attrs->{as} }, @asadds ]; + } my $collapse = $attrs->{collapse} || {}; if (my $prefetch = delete $attrs->{prefetch}) { my @pre_order; @@ -740,10 +754,46 @@ sub _resolve { push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) unless $seen{$p}; } - my @prefetch = $source->resolve_prefetch( + + # we're about to resolve_join on the current class, so we need to bring + # the joins (which are from the original class) to the right level + # XXX the below alg is ridiculous + if ($attrs->{_live_join_stack}) { + STACK: foreach (@{$attrs->{_live_join_stack}}) { + if (ref $p eq 'HASH') { + if (exists $p->{$_}) { + $p = $p->{$_}; + } else { + $p = undef; + last STACK; + } + } elsif (ref $p eq 'ARRAY') { + foreach my $pe (@{$p}) { + if ($pe eq $_) { + $p = undef; + last STACK; + } + next unless(ref $pe eq 'HASH'); + next unless(exists $pe->{$_}); + $p = $pe->{$_}; + next STACK; + } + $p = undef; + last STACK; + } else { + $p = undef; + last STACK; + } + } + } + + if ($p) { + my @prefetch = $self->result_source->resolve_prefetch( $p, $attrs->{alias}, {}, \@pre_order, $collapse); - push(@{$attrs->{select}}, map { $_->[0] } @prefetch); - push(@{$attrs->{as}}, map { $_->[1] } @prefetch); + + push(@{$attrs->{select}}, map { $_->[0] } @prefetch); + push(@{$attrs->{as}}, map { $_->[1] } @prefetch); + } } push(@{$attrs->{order_by}}, @pre_order); } @@ -756,64 +806,64 @@ sub _merge_attr { return $b unless $a; if (ref $b eq 'HASH' && ref $a eq 'HASH') { - foreach my $key (keys %{$b}) { - if (exists $a->{$key}) { - $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key}, $is_prefetch); - } else { - $a->{$key} = delete $b->{$key}; - } - } - return $a; + foreach my $key (keys %{$b}) { + if (exists $a->{$key}) { + $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key}, $is_prefetch); + } else { + $a->{$key} = $b->{$key}; + } + } + return $a; } else { - $a = [$a] unless (ref $a eq 'ARRAY'); - $b = [$b] unless (ref $b eq 'ARRAY'); - - my $hash = {}; - my $array = []; - foreach ($a, $b) { - foreach my $element (@{$_}) { - if (ref $element eq 'HASH') { - $hash = $self->_merge_attr($hash, $element, $is_prefetch); - } elsif (ref $element eq 'ARRAY') { - $array = [@{$array}, @{$element}]; - } else { - if (($b == $_) && $is_prefetch) { - $self->_merge_array($array, $element, $is_prefetch); - } else { - push(@{$array}, $element); - } - } - } - } - if ($is_prefetch) { - my $final_array = []; - foreach my $element (@{$array}) { - push(@{$final_array}, $element) unless (exists $hash->{$element}); - } - $array = $final_array; - } - if ((keys %{$hash}) && (scalar(@{$array} > 0))) { - return [$hash, @{$array}]; - } else { - return (keys %{$hash}) ? $hash : $array; + $a = [$a] unless (ref $a eq 'ARRAY'); + $b = [$b] unless (ref $b eq 'ARRAY'); + + my $hash = {}; + my $array = []; + foreach ($a, $b) { + foreach my $element (@{$_}) { + if (ref $element eq 'HASH') { + $hash = $self->_merge_attr($hash, $element, $is_prefetch); + } elsif (ref $element eq 'ARRAY') { + $array = [@{$array}, @{$element}]; + } else { + if (($b == $_) && $is_prefetch) { + $self->_merge_array($array, $element, $is_prefetch); + } else { + push(@{$array}, $element); + } + } + } + } + + my $final_array = []; + foreach my $element (@{$array}) { + push(@{$final_array}, $element) unless (exists $hash->{$element}); } + $array = $final_array; + + if ((keys %{$hash}) && (scalar(@{$array} > 0))) { + return [$hash, @{$array}]; + } else { + return (keys %{$hash}) ? $hash : $array; + } } } sub _merge_array { - my ($self, $a, $b) = @_; - - $b = [$b] unless (ref $b eq 'ARRAY'); - # add elements from @{$b} to @{$a} which aren't already in @{$a} - foreach my $b_element (@{$b}) { - push(@{$a}, $b_element) unless grep {$b_element eq $_} @{$a}; - } + my ($self, $a, $b) = @_; + + $b = [$b] unless (ref $b eq 'ARRAY'); + # add elements from @{$b} to @{$a} which aren't already in @{$a} + foreach my $b_element (@{$b}) { + push(@{$a}, $b_element) unless grep {$b_element eq $_} @{$a}; + } } sub _construct_object { my ($self, @row) = @_; my @as = @{ $self->{_attrs}{as} }; - + my $info = $self->_collapse_result(\@as, \@row); my $new = $self->result_class->inflate_result($self->result_source, @$info); $new = $self->{_attrs}{record_filter}->($new) @@ -855,8 +905,8 @@ sub _collapse_result { $info->[0] = $const{$key}; } } - my @collapse; + if (defined $prefix) { @collapse = map { m/^\Q${prefix}.\E(.+)$/ ? ($1) : () @@ -873,9 +923,10 @@ sub _collapse_result { } my $c_prefix = (defined($prefix) ? "${prefix}.${c}" : $c); my @co_key = @{$self->{_attrs}->{collapse}{$c_prefix}}; - my %co_check = map { ($_, $target->[0]->{$_}); } @co_key; my $tree = $self->_collapse_result($as, $row, $c_prefix); + my %co_check = map { ($_, $tree->[0]->{$_}); } @co_key; my (@final, @raw); + while ( !(grep { !defined($tree->[0]->{$_}) || $co_check{$_} ne $tree->[0]->{$_} @@ -888,6 +939,8 @@ sub _collapse_result { @$target = (@final ? @final : [ {}, {} ]); # single empty result to indicate an empty prefetched has_many } + + #print "final info: " . Dumper($info); return $info; } @@ -1551,28 +1604,34 @@ Returns a related resultset for the supplied relationship name. sub related_resultset { my ( $self, $rel ) = @_; - + $self->{related_resultsets} ||= {}; return $self->{related_resultsets}{$rel} ||= do { - #warn "fetching related resultset for rel '$rel' " . $self->result_source->{name}; - my $rel_obj = $self->result_source->relationship_info($rel); - $self->throw_exception( + #warn "fetching related resultset for rel '$rel' " . $self->result_source->{name}; + my $rel_obj = $self->result_source->relationship_info($rel); + #print Dumper($self->result_source->_relationships); + $self->throw_exception( "search_related: result source '" . $self->result_source->name . "' has no such relationship ${rel}") unless $rel_obj; #die Dumper $self->{attrs}; - my $rs = $self->result_source->schema->resultset($rel_obj->{class} + my @live_join_stack = (exists $self->{attrs}->{_live_join_stack}) ? + @{$self->{attrs}->{_live_join_stack}}: + (); + push(@live_join_stack, $rel); + + my $rs = $self->result_source->schema->resultset($rel_obj->{class} )->search( undef, - { %{$self->{attrs}}, - select => undef, - as => undef, - join => $rel, - _live_join => $rel } - ); - - # keep reference of the original resultset - $rs->{_parent_rs} = $self->result_source; - return $rs; + { select => undef, + as => undef, + _live_join => $rel, #the most recent + _live_join_stack => \@live_join_stack, #the trail of rels + _parent_attrs => $self->{attrs}} + ); + + # keep reference of the original resultset + $rs->{_parent_rs} = ($self->{_parent_rs}) ? $self->{_parent_rs} : $self->result_source; + return $rs; }; }