1 package DBIx::Class::ResultSet;
15 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
19 my $rs = MyApp::DB::Class->search(registered => 1);
20 my @rows = MyApp::DB::Class->search(foo => 'bar');
24 The resultset is also known as an iterator. It is responsible for handling
25 queries that may return an arbitrary number of rows, e.g. via C<search>
26 or a C<has_many> relationship.
30 =head2 new($source, \%$attrs)
32 The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
33 and an attribute hash (see below for more information on attributes). Does
34 not perform any queries -- these are executed as needed by the other methods.
40 return $class->new_result(@_) if ref $class;
41 my ($source, $attrs) = @_;
42 #use Data::Dumper; warn Dumper($attrs);
43 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
45 my $alias = ($attrs->{alias} ||= 'me');
46 if ($attrs->{cols} || !$attrs->{select}) {
47 delete $attrs->{as} if $attrs->{cols};
48 my @cols = ($attrs->{cols}
49 ? @{delete $attrs->{cols}}
51 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
53 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
54 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
55 $attrs->{from} ||= [ { $alias => $source->from } ];
56 if (my $join = delete $attrs->{join}) {
57 foreach my $j (ref $join eq 'ARRAY'
58 ? (@{$join}) : ($join)) {
59 if (ref $j eq 'HASH') {
60 $seen{$_} = 1 foreach keys %$j;
65 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
67 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
68 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
69 push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias}))
73 $source->related_source($pre)->columns;
74 push(@{$attrs->{select}}, @pre);
75 push(@{$attrs->{as}}, @pre);
78 $attrs->{rows} ||= 10;
79 $attrs->{offset} ||= 0;
80 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
84 cond => $attrs->{where},
85 from => $attrs->{from},
87 page => delete $attrs->{page},
96 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
97 my $new_rs = $rs->search({ foo => 3 });
99 If you need to pass in additional attributes but no additional condition,
100 call it as ->search(undef, \%attrs);
102 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
109 #use Data::Dumper;warn Dumper(@_);
111 my $attrs = { %{$self->{attrs}} };
112 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
113 $attrs = { %$attrs, %{ pop(@_) } };
116 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
117 if (defined $where) {
118 $where = (defined $attrs->{where}
120 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
121 $where, $attrs->{where} ] }
123 $attrs->{where} = $where;
126 my $rs = (ref $self)->new($self->{source}, $attrs);
128 return (wantarray ? $rs->all : $rs);
131 =head2 search_literal
132 my @obj = $rs->search_literal($literal_where_cond, @bind);
133 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
135 Pass a literal chunk of SQL to be added to the conditional part of the
141 my ($self, $cond, @vals) = @_;
142 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
143 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
144 return $self->search(\$cond, $attrs);
147 =head2 find(@colvalues), find(\%cols)
149 Finds a row based on its primary key(s).
154 my ($self, @vals) = @_;
155 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
156 my @pk = $self->{source}->primary_columns;
157 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
158 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
161 if (ref $vals[0] eq 'HASH') {
163 } elsif (@pk == @vals) {
165 @{$query}{@pk} = @vals;
169 #warn Dumper($query);
170 # Useless -> disabled
171 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
172 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
173 # column names etc. Not sure what to do yet
174 return $self->search($query)->next;
177 =head2 search_related
179 $rs->search_related('relname', $cond?, $attrs?);
184 my ($self, $rel, @rest) = @_;
185 my $rel_obj = $self->{source}->relationship_info($rel);
186 $self->{source}->result_class->throw(
187 "No such relationship ${rel} in search_related")
189 my $rs = $self->search(undef, { join => $rel });
190 return $self->{source}->schema->resultset($rel_obj->{class}
201 Returns a storage-driven cursor to the given resultset.
207 my ($source, $attrs) = @{$self}{qw/source attrs/};
208 $attrs = { %$attrs };
209 return $self->{cursor}
210 ||= $source->storage->select($self->{from}, $attrs->{select},
211 $attrs->{where},$attrs);
216 Identical to search except defaults to 'LIKE' instead of '=' in condition
223 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
226 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
227 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
228 return $class->search($query, { %$attrs });
231 =head2 slice($first, $last)
233 Returns a subset of elements from the resultset.
238 my ($self, $min, $max) = @_;
239 my $attrs = { %{ $self->{attrs} || {} } };
240 $attrs->{offset} ||= 0;
241 $attrs->{offset} += $min;
242 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
243 my $slice = (ref $self)->new($self->{source}, $attrs);
244 return (wantarray ? $slice->all : $slice);
249 Returns the next element in the resultset (undef is there is none).
255 my @row = $self->cursor->next;
256 return unless (@row);
257 return $self->_construct_object(@row);
260 sub _construct_object {
261 my ($self, @row) = @_;
262 my @cols = @{ $self->{attrs}{as} };
263 #warn "@cols -> @row";
265 foreach my $col (@cols) {
266 if ($col =~ /([^\.]+)\.([^\.]+)/) {
267 $pre{$1}[0]{$2} = shift @row;
269 $me{$col} = shift @row;
272 my $new = $self->{source}->result_class->inflate_result(
273 $self->{source}, \%me, \%pre);
274 $new = $self->{attrs}{record_filter}->($new)
275 if exists $self->{attrs}{record_filter};
281 Performs an SQL C<COUNT> with the same query as the resultset was built
282 with to find the number of elements. If passed arguments, does a search
283 on the resultset and counts the results of that.
289 return $self->search(@_)->count if @_ && defined $_[0];
290 croak "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
291 unless (defined $self->{count}) {
292 my $attrs = { %{ $self->{attrs} },
293 select => { 'count' => '*' },
295 # offset, order by and page are not needed to count. record_filter is cdbi
296 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
298 ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next;
300 return 0 unless $self->{count};
301 my $count = $self->{count};
302 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
303 $count = $self->{attrs}{rows} if
304 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
310 Calls search_literal with the passed arguments, then count.
314 sub count_literal { shift->search_literal(@_)->count; }
318 Returns all elements in the resultset. Called implictly if the resultset
319 is returned in list context.
325 return map { $self->_construct_object(@$_); }
331 Resets the resultset's cursor, so you can iterate through the elements again.
337 $self->cursor->reset;
343 Resets the resultset and returns the first element.
348 return $_[0]->reset->next;
351 =head2 update(\%values)
353 Sets the specified columns in the resultset to the supplied values
358 my ($self, $values) = @_;
359 croak "Values for update must be a hash" unless ref $values eq 'HASH';
360 return $self->{source}->storage->update(
361 $self->{source}->from, $values, $self->{cond});
364 =head2 update_all(\%values)
366 Fetches all objects and updates them one at a time. ->update_all will run
367 cascade triggers, ->update will not.
372 my ($self, $values) = @_;
373 croak "Values for update must be a hash" unless ref $values eq 'HASH';
374 foreach my $obj ($self->all) {
375 $obj->set_columns($values)->update;
382 Deletes the contents of the resultset from its result source.
388 $self->{source}->storage->delete($self->{source}->from, $self->{cond});
394 Fetches all objects and deletes them one at a time. ->delete_all will run
395 cascade triggers, ->delete will not.
401 $_->delete for $self->all;
407 Returns a L<Data::Page> object for the current resultset. Only makes
408 sense for queries with page turned on.
414 my $attrs = $self->{attrs};
415 croak "Can't create pager for non-paged rs" unless $self->{page};
416 $attrs->{rows} ||= 10;
418 return $self->{pager} ||= Data::Page->new(
419 $self->{count}, $attrs->{rows}, $self->{page});
422 =head2 page($page_num)
424 Returns a new resultset for the specified page.
429 my ($self, $page) = @_;
430 my $attrs = { %{$self->{attrs}} };
431 $attrs->{page} = $page;
432 return (ref $self)->new($self->{source}, $attrs);
435 =head2 new_result(\%vals)
437 Creates a result in the resultset's result class
442 my ($self, $values) = @_;
443 $self->{source}->result_class->throw( "new_result needs a hash" )
444 unless (ref $values eq 'HASH');
445 $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" )
446 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
448 my $alias = $self->{attrs}{alias};
449 foreach my $key (keys %{$self->{cond}||{}}) {
450 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
452 my $obj = $self->{source}->result_class->new(\%new);
453 $obj->result_source($self->{source}) if $obj->can('result_source');
457 =head2 create(\%vals)
459 Inserts a record into the resultset and returns the object
461 Effectively a shortcut for ->new_result(\%vals)->insert
466 my ($self, $attrs) = @_;
467 $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH';
468 return $self->new_result($attrs)->insert;
471 =head2 find_or_create(\%vals)
473 $class->find_or_create({ key => $val, ... });
475 Searches for a record matching the search condition; if it doesn't find one,
476 creates one and returns that instead.
482 my $hash = ref $_[0] eq "HASH" ? shift: {@_};
483 my $exists = $self->find($hash);
484 return defined($exists) ? $exists : $self->create($hash);
489 The resultset takes various attributes that modify its behavior.
490 Here's an overview of them:
494 Which column(s) to order the results by. This is currently passed
495 through directly to SQL, so you can give e.g. C<foo DESC> for a
498 =head2 cols (arrayref)
500 Shortcut to request a particular set of columns to be retrieved - adds
501 'me.' onto the start of any column without a '.' in it and sets 'select'
502 from that, then auto-populates 'as' from 'select' as normal
504 =head2 select (arrayref)
506 Indicates which columns should be selected from the storage
510 Indicates column names for object inflation
514 Contains a list of relationships that should be joined for this query. Can also
515 contain a hash reference to refer to that relation's relations. So, if one column
516 in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
517 C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
518 If a foo contains many margles and you want to join those too, you can do
519 C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
520 related table as well, see C<prefetch> below.
524 Contains a list of relationships that should be fetched along with the main
525 query (when they are accessed afterwards they will have already been
526 "prefetched"). This is useful for when you know you will need the related
527 object(s), because it saves a query. Currently limited to prefetching
528 one relationship deep, so unlike C<join>, prefetch must be an arrayref.
532 This attribute can contain a arrayref of elements. Each element can be another
533 arrayref, to nest joins, or it can be a hash which represents the two sides
536 NOTE: Use this on your own risk. This allows you to shoot your foot off!
540 For a paged resultset, specifies which page to retrieve. Leave unset
541 for an unpaged resultset.
545 For a paged resultset, how many rows per page
547 =head2 group_by (listref)
549 A listref of columns to group by (note that 'count' doesn't work on grouped
552 group_by => [qw/ column1 column2 ... /]
556 Set to 1 to group by all columns