X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=dc845f77e9609a7f0bc3269fd234f8b49502ebf0;hb=b2f17732851595fc74d4850fa75b34fa52d19b0a;hp=4e5d1ee5af7fc93c4bbb15332f07e79a464c44a7;hpb=84e3c1143175058ea5d3d24eb31d529be09a6806;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 4e5d1ee..dc845f7 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -21,7 +21,7 @@ DBIx::Class::ResultSet - Responsible for fetching and creating resultset. =head1 SYNOPSIS my $rs = $schema->resultset('User')->search(registered => 1); - my @rows = $schema->resultset('Foo')->search(bar => 'baz'); + my @rows = $schema->resultset('CD')->search(year => 2005); =head1 DESCRIPTION @@ -53,12 +53,16 @@ In the examples below, the following table classes are used: =head2 new -=head3 Arguments: ($source, \%$attrs) +=over 4 + +=item Arguments: ($source, \%$attrs) + +=back The resultset constructor. Takes a source object (usually a -L) and an attribute hash (see L -below). Does not perform any queries -- these are executed as needed by the -other methods. +L) and an attribute hash (see +L below). Does not perform any queries -- these are +executed as needed by the other methods. Generally you won't need to construct a resultset manually. You'll automatically get one from e.g. a L called in scalar context: @@ -80,9 +84,12 @@ sub new { $attrs->{columns} ||= delete $attrs->{cols} if $attrs->{cols}; delete $attrs->{as} if $attrs->{columns}; $attrs->{columns} ||= [ $source->columns ] unless $attrs->{select}; - $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @{delete $attrs->{columns}} ] - if $attrs->{columns}; - $attrs->{as} ||= [ map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}} ]; + $attrs->{select} = [ + map { m/\./ ? $_ : "${alias}.$_" } @{delete $attrs->{columns}} + ] if $attrs->{columns}; + $attrs->{as} ||= [ + map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}} + ]; if (my $include = delete $attrs->{include_columns}) { push(@{$attrs->{select}}, @$include); push(@{$attrs->{as}}, map { m/([^.]+)$/; $1; } @$include); @@ -100,11 +107,14 @@ sub new { $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} = [ $attrs->{order_by} ] if + $attrs->{order_by} and !ref($attrs->{order_by}); $attrs->{order_by} ||= []; my $collapse = $attrs->{collapse} || {}; @@ -151,14 +161,24 @@ sub new { =head2 search - my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3" - my $new_rs = $rs->search({ foo => 3 }); +=over 4 + +=item Arguments: (\%cond?, \%attrs?) + +=item Returns: $resultset (scalar context), @row_objs (list context) + +=back + + my @cds = $cd_rs->search({ year => 2001 }); # "... WHERE year = 2001" + my $new_rs = $cd_rs->search({ year => 2005 }); If you need to pass in additional attributes but no additional condition, call it as C. - # "SELECT foo, bar FROM $class_table" - my @all = $class->search(undef, { columns => [qw/foo bar/] }); + # "SELECT name, artistid FROM $artist_table" + my @all_artists = $schema->resultset('Artist')->search(undef, { + columns => [qw/name artistid/], + }); =cut @@ -207,11 +227,19 @@ sub search { =head2 search_literal - my @obj = $rs->search_literal($literal_where_cond, @bind); - my $new_rs = $rs->search_literal($literal_where_cond, @bind); +=over 4 + +=item Arguments: ($literal_cond, @bind?) + +=item Returns: $resultset (scalar context), @row_objs (list context) + +=back + + my @cds = $cd_rs->search_literal('year = ? AND title = ?', qw/2001 Reload/); + my $newrs = $artist_rs->search_literal('name = ?', 'Metallica'); Pass a literal chunk of SQL to be added to the conditional part of the -resultset. +resultset query. =cut @@ -224,7 +252,13 @@ sub search_literal { =head2 find -=head3 Arguments: (@colvalues) | (\%cols, \%attrs?) +=over 4 + +=item Arguments: (@colvalues) | (\%cols, \%attrs?) + +=item Returns: $row_object + +=back Finds a row based on its primary key or unique constraint. For example: @@ -252,13 +286,15 @@ sub find { my @cols = $self->result_source->primary_columns; if (exists $attrs->{key}) { my %uniq = $self->result_source->unique_constraints; - $self->throw_exception( "Unknown key $attrs->{key} on $self->name" ) - unless exists $uniq{$attrs->{key}}; + $self->throw_exception( + "Unknown key $attrs->{key} on '" . $self->result_source->name . "'" + ) unless exists $uniq{$attrs->{key}}; @cols = @{ $uniq{$attrs->{key}} }; } #use Data::Dumper; warn Dumper($attrs, @vals, @cols); - $self->throw_exception( "Can't find unless a primary key or unique constraint is defined" ) - unless @cols; + $self->throw_exception( + "Can't find unless a primary key or unique constraint is defined" + ) unless @cols; my $query; if (ref $vals[0] eq 'HASH') { @@ -278,16 +314,28 @@ sub find { my $rs = $self->search($query,$attrs); return keys %{$rs->{collapse}} ? $rs->next : $rs->single; } else { - return keys %{$self->{collapse}} ? $self->search($query)->next : $self->single($query); + return keys %{$self->{collapse}} ? + $self->search($query)->next : + $self->single($query); } } =head2 search_related - $rs->search_related('relname', $cond?, $attrs?); +=over 4 -Search the specified relationship. Optionally specify a condition for matching -records. +=item Arguments: (\%cond?, \%attrs?) + +=item Returns: $new_resultset + +=back + + $new_rs = $cd_rs->search_related('artist', { + name => 'Emo-R-Us', + }); + +Search the specified relationship, optionally specify a condition and +attributes for matching records. See L for more information. =cut @@ -297,7 +345,16 @@ sub search_related { =head2 cursor -Returns a storage-driven cursor to the given resultset. +=over 4 + +=item Arguments: (none) + +=item Returns: $cursor + +=back + +Returns a storage-driven cursor to the given resultset. See +L for more information. =cut @@ -311,7 +368,17 @@ sub cursor { =head2 single -Inflates the first result without creating a cursor +=over 4 + +=item Arguments: (\%cond) + +=item Returns: $row_object + +=back + + my $cd = $schema->resultset('CD')->single({ year => 2001 }); + +Inflates the first result without creating a cursor. =cut @@ -338,8 +405,19 @@ sub single { =head2 search_like -Perform a search, but use C instead of equality as the condition. Note -that this is simply a convenience method; you most likely want to use +=over 4 + +=item Arguments: (\%cond?, \%attrs?) + +=item Returns: $resultset (scalar context), @row_objs (list context) + +=back + + # WHERE title LIKE '%blue%' + $cd_rs = $rs->search_like({ title => '%blue%'}); + +Perform a search, but use C instead of C<=> as the condition. Note +that this is simply a convenience method. You most likely want to use L with specific operators. For more information, see L. @@ -356,7 +434,13 @@ sub search_like { =head2 slice -=head3 Arguments: ($first, $last) +=over 4 + +=item Arguments: ($first, $last) + +=item Returns: $resultset (scalar context), @row_objs (list context) + +=back Returns a subset of elements from the resultset. @@ -395,9 +479,10 @@ sub next { $self->{all_cache_position} = 1; return ($self->all)[0]; } - my @row = (exists $self->{stashed_row} - ? @{delete $self->{stashed_row}} - : $self->cursor->next); + my @row = (exists $self->{stashed_row} ? + @{delete $self->{stashed_row}} : + $self->cursor->next + ); # warn Dumper(\@row); use Data::Dumper; return unless (@row); return $self->_construct_object(@row); @@ -450,10 +535,15 @@ sub _collapse_result { } } - my @collapse = (defined($prefix) - ? (map { (m/^\Q${prefix}.\E(.+)$/ ? ($1) : ()); } - keys %{$self->{collapse}}) - : keys %{$self->{collapse}}); + my @collapse; + if (defined $prefix) { + @collapse = map { + m/^\Q${prefix}.\E(.+)$/ ? ($1) : () + } keys %{$self->{collapse}} + } else { + @collapse = keys %{$self->{collapse}}; + }; + if (@collapse) { my ($c) = sort { length $a <=> length $b } @collapse; my $target = $info; @@ -466,8 +556,8 @@ sub _collapse_result { my $tree = $self->_collapse_result($as, $row, $c_prefix); my (@final, @raw); while ( !(grep { - !defined($tree->[0]->{$_}) - || $co_check{$_} ne $tree->[0]->{$_} + !defined($tree->[0]->{$_}) || + $co_check{$_} ne $tree->[0]->{$_} } @co_key) ) { push(@final, $tree); last unless (@raw = $self->cursor->next); @@ -550,7 +640,8 @@ sub _count { # Separated out so pager can get the full count =head2 count_literal -Calls L with the passed arguments, then L. +Counts the results in a literal query. Equivalent to calling L +with the passed arguments, then L. =cut @@ -558,7 +649,7 @@ sub count_literal { shift->search_literal(@_)->count; } =head2 all -Returns all elements in the resultset. Called implictly if the resultset +Returns all elements in the resultset. Called implicitly if the resultset is returned in list context. =cut @@ -615,7 +706,11 @@ sub first { =head2 update -=head3 Arguments: (\%values) +=over 4 + +=item Arguments: (\%values) + +=back Sets the specified columns in the resultset to the supplied values. @@ -623,14 +718,20 @@ Sets the specified columns in the resultset to the supplied values. sub update { my ($self, $values) = @_; - $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH'; + $self->throw_exception("Values for update must be a hash") + unless ref $values eq 'HASH'; return $self->result_source->storage->update( - $self->result_source->from, $values, $self->{cond}); + $self->result_source->from, $values, $self->{cond} + ); } =head2 update_all -=head3 Arguments: (\%values) +=over 4 + +=item Arguments: (\%values) + +=back Fetches all objects and updates them one at a time. Note that C will run cascade triggers while L will not. @@ -639,7 +740,8 @@ will run cascade triggers while L will not. sub update_all { my ($self, $values) = @_; - $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH'; + $self->throw_exception("Values for update must be a hash") + unless ref $values eq 'HASH'; foreach my $obj ($self->all) { $obj->set_columns($values)->update; } @@ -648,33 +750,51 @@ sub update_all { =head2 delete -Deletes the contents of the resultset from its result source. +Deletes the contents of the resultset from its result source. Note that this +will not run cascade triggers. See L if you need triggers to run. =cut sub delete { my ($self) = @_; my $del = {}; - $self->throw_exception("Can't delete on resultset with condition unless hash or array") - unless (ref($self->{cond}) eq 'HASH' || ref($self->{cond}) eq 'ARRAY'); - if (ref $self->{cond} eq 'ARRAY') { + + if (!ref($self->{cond})) { + + # No-op. No condition, we're deleting everything + + } elsif (ref $self->{cond} eq 'ARRAY') { + $del = [ map { my %hash; foreach my $key (keys %{$_}) { $key =~ /([^.]+)$/; $hash{$1} = $_->{$key}; }; \%hash; } @{$self->{cond}} ]; - } elsif ((keys %{$self->{cond}})[0] eq '-and') { - $del->{-and} = [ map { my %hash; - foreach my $key (keys %{$_}) { + + } elsif (ref $self->{cond} eq 'HASH') { + + if ((keys %{$self->{cond}})[0] eq '-and') { + + $del->{-and} = [ map { my %hash; + foreach my $key (keys %{$_}) { + $key =~ /([^.]+)$/; + $hash{$1} = $_->{$key}; + }; \%hash; } @{$self->{cond}{-and}} ]; + + } else { + + foreach my $key (keys %{$self->{cond}}) { $key =~ /([^.]+)$/; - $hash{$1} = $_->{$key}; - }; \%hash; } @{$self->{cond}{-and}} ]; - } else { - foreach my $key (keys %{$self->{cond}}) { - $key =~ /([^.]+)$/; - $del->{$1} = $self->{cond}{$key}; + $del->{$1} = $self->{cond}{$key}; + } } + + } else { + $self->throw_exception( + "Can't delete on resultset with condition unless hash or array" + ); } + $self->result_source->storage->delete($self->result_source->from, $del); return 1; } @@ -702,7 +822,8 @@ sense for queries with a C attribute. sub pager { my ($self) = @_; my $attrs = $self->{attrs}; - $self->throw_exception("Can't create pager for non-paged rs") unless $self->{page}; + $self->throw_exception("Can't create pager for non-paged rs") + unless $self->{page}; $attrs->{rows} ||= 10; return $self->{pager} ||= Data::Page->new( $self->_count, $attrs->{rows}, $self->{page}); @@ -710,7 +831,11 @@ sub pager { =head2 page -=head3 Arguments: ($page_num) +=over 4 + +=item Arguments: ($page_num) + +=back Returns a new resultset for the specified page. @@ -725,7 +850,11 @@ sub page { =head2 new_result -=head3 Arguments: (\%vals) +=over 4 + +=item Arguments: (\%vals) + +=back Creates a result in the resultset's result class. @@ -735,8 +864,9 @@ sub new_result { my ($self, $values) = @_; $self->throw_exception( "new_result needs a hash" ) unless (ref $values eq 'HASH'); - $self->throw_exception( "Can't abstract implicit construct, condition not a hash" ) - if ($self->{cond} && !(ref $self->{cond} eq 'HASH')); + $self->throw_exception( + "Can't abstract implicit construct, condition not a hash" + ) if ($self->{cond} && !(ref $self->{cond} eq 'HASH')); my %new = %$values; my $alias = $self->{attrs}{alias}; foreach my $key (keys %{$self->{cond}||{}}) { @@ -749,7 +879,11 @@ sub new_result { =head2 create -=head3 Arguments: (\%vals) +=over 4 + +=item Arguments: (\%vals) + +=back Inserts a record into the resultset and returns the object. @@ -759,13 +893,18 @@ Effectively a shortcut for C<< ->new_result(\%vals)->insert >>. sub create { my ($self, $attrs) = @_; - $self->throw_exception( "create needs a hashref" ) unless ref $attrs eq 'HASH'; + $self->throw_exception( "create needs a hashref" ) + unless ref $attrs eq 'HASH'; return $self->new_result($attrs)->insert; } =head2 find_or_create -=head3 Arguments: (\%vals, \%attrs?) +=over 4 + +=item Arguments: (\%vals, \%attrs?) + +=back $class->find_or_create({ key => $val, ... }); @@ -879,7 +1018,8 @@ sub get_cache { =head2 set_cache -Sets the contents of the cache for the resultset. Expects an arrayref of objects of the same class as those produced by the resultset. +Sets the contents of the cache for the resultset. Expects an arrayref +of objects of the same class as those produced by the resultset. =cut @@ -889,8 +1029,9 @@ sub set_cache { if ref $data ne 'ARRAY'; my $result_class = $self->result_class; foreach( @$data ) { - $self->throw_exception("cannot cache object of type '$_', expected '$result_class'") - if ref $_ ne $result_class; + $self->throw_exception( + "cannot cache object of type '$_', expected '$result_class'" + ) if ref $_ ne $result_class; } $self->{all_cache} = $data; } @@ -909,7 +1050,7 @@ sub clear_cache { Returns a related resultset for the supplied relationship name. - $rs = $rs->related_resultset('foo'); + $artist_rs = $schema->resultset('CD')->related_resultset('Artist'); =cut @@ -953,17 +1094,24 @@ sub throw_exception { =head1 ATTRIBUTES +XXX: FIXME: Attributes docs need clearing up + The resultset takes various attributes that modify its behavior. Here's an overview of them: =head2 order_by -Which column(s) to order the results by. This is currently passed through -directly to SQL, so you can give e.g. C for a descending order. +Which column(s) to order the results by. This is currently passed +through directly to SQL, so you can give e.g. C for a +descending order on the column `year'. =head2 columns -=head3 Arguments: (arrayref) +=over 4 + +=item Arguments: (\@columns) + +=back Shortcut to request a particular set of columns to be retrieved. Adds C onto the start of any column without a C<.> in it and sets C, usually when C