X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=db246df4fa02f476432d275c2b235b4be3b63bc8;hb=48c9af026b923ad5d18542ae9a0a5f7ccae5ea35;hp=89600cebcccd44e397c93b2f6dd9b7587c58c174;hpb=addaea392490128eb78b8d5193f0c933a3922717;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 89600ce..db246df 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -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 @@ -263,7 +278,7 @@ a row by its primary key: You can also find a row by a specific unique constraint using the C attribute. For example: - my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'artist_title' }); + my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'cd_artist_title' }); Additionally, you can specify the columns explicitly by name: @@ -272,7 +287,7 @@ Additionally, you can specify the columns explicitly by name: artist => 'Massive Attack', title => 'Mezzanine', }, - { key => 'artist_title' } + { key => 'cd_artist_title' } ); If the C is specified as C, it searches only on the primary key. @@ -314,15 +329,10 @@ sub find { } my @unique_queries = $self->_unique_queries($input_query, $attrs); -# use Data::Dumper; warn Dumper $self->result_source->name, $input_query, \@unique_queries, $self->{attrs}->{where}; - - # Verify the query - my $query = \@unique_queries; - if (scalar @unique_queries == 0) { - # Handle cases where the ResultSet defines the query, or where the user is - # abusing find - $query = $input_query; - } + + # Handle cases where the ResultSet defines the query, or where the user is + # abusing find + my $query = @unique_queries ? \@unique_queries : $input_query; # Run the query if (keys %$attrs) { @@ -358,7 +368,8 @@ sub _unique_queries { # Add the ResultSet's alias foreach my $key (grep { ! m/\./ } keys %$unique_query) { - $unique_query->{"$self->{attrs}->{alias}.$key"} = delete $unique_query->{$key}; + my $alias = ($self->{attrs}->{_live_join}) ? $self->{attrs}->{_live_join} : $self->{attrs}->{alias}; + $unique_query->{"$alias.$key"} = delete $unique_query->{$key}; } push @unique_queries, $unique_query; @@ -467,7 +478,6 @@ sub single { } } -# use Data::Dumper; warn Dumper $attrs->{where}; unless ($self->_is_unique_query($attrs->{where})) { carp "Query not guarnteed to return a single row" . "; please declare your unique constraints or use search instead"; @@ -488,18 +498,21 @@ sub _is_unique_query { my ($self, $query) = @_; my $collapsed = $self->_collapse_query($query); -# use Data::Dumper; warn Dumper $collapsed; + my $alias = ($self->{attrs}->{_live_join}) ? $self->{attrs}->{_live_join} : $self->{attrs}->{alias}; foreach my $name ($self->result_source->unique_constraint_names) { - my @unique_cols = map { "$self->{attrs}->{alias}.$_" } + my @unique_cols = map { "$alias.$_" } $self->result_source->unique_constraint_columns($name); # Count the values for each unique column my %seen = map { $_ => 0 } @unique_cols; foreach my $key (keys %$collapsed) { - next unless exists $seen{$key}; # Additional constraints are okay - $seen{$key} = scalar @{ $collapsed->{$key} }; + my $aliased = $key; + $aliased = "$alias.$key" unless $key =~ /\./; + + next unless exists $seen{$aliased}; # Additional constraints are okay + $seen{$aliased} = scalar @{ $collapsed->{$key} }; } # If we get 0 or more than 1 value for a column, it's not necessarily unique @@ -516,7 +529,6 @@ sub _is_unique_query { sub _collapse_query { my ($self, $query, $collapsed) = @_; - # Accumulate fields in the AST $collapsed ||= {}; if (ref $query eq 'ARRAY') { @@ -538,7 +550,6 @@ sub _collapse_query { foreach my $key (keys %$query) { push @{$collapsed->{$key}}, $query->{$key}; } -# warn Dumper $collapsed; } } @@ -674,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 @@ -704,32 +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; - } + 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})); + } + + 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}) { @@ -760,58 +771,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} = delete $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); - } - } - } - } + $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 ((keys %{$hash}) && (scalar(@{$array} > 0))) { - return [$hash, @{$array}]; - } else { - return (keys %{$hash}) ? $hash : $array; + 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) @@ -931,7 +948,6 @@ sub count { my $self = shift; return $self->search(@_)->count if @_ and defined $_[0]; return scalar @{ $self->get_cache } if $self->get_cache; - my $count = $self->_count; return 0 unless $count; @@ -969,7 +985,10 @@ sub _count { # Separated out so pager can get the full count # offset, order by and page are not needed to count. record_filter is cdbi delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/; - my ($count) = (ref $self)->new($self->result_source, $attrs)->cursor->next; + my $tmp_rs = (ref $self)->new($self->result_source, $attrs); + $tmp_rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs}); #XXX - hack to pass through parent of related resultsets + + my ($count) = $tmp_rs->cursor->next; return $count; } @@ -1399,7 +1418,7 @@ constraint. For example: artist => 'Massive Attack', title => 'Mezzanine', }, - { key => 'artist_title' } + { key => 'cd_artist_title' } ); See also L and L. For information on how to declare @@ -1442,7 +1461,7 @@ For example: title => 'Mezzanine', year => 1998, }, - { key => 'artist_title' } + { key => 'cd_artist_title' } ); If no C is specified, it searches on all unique constraints defined on the @@ -1547,28 +1566,31 @@ 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); + $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 = $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; }; }