1 package DBIx::Class::ResultSet;
14 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
18 my $rs = MyApp::DB::Class->search(registered => 1);
19 my @rows = MyApp::DB::Class->search(foo => 'bar');
23 The resultset is also known as an iterator. It is responsible for handling
24 queries that may return an arbitrary number of rows, e.g. via C<search>
25 or a C<has_many> relationship.
29 =head2 new($source, \%$attrs)
31 The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
32 and an attribute hash (see below for more information on attributes). Does
33 not perform any queries -- these are executed as needed by the other methods.
39 return $class->new_result(@_) if ref $class;
40 my ($source, $attrs) = @_;
41 #use Data::Dumper; warn Dumper($attrs);
42 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
44 my $alias = ($attrs->{alias} ||= 'me');
45 if (!$attrs->{select}) {
46 my @cols = ($attrs->{cols}
47 ? @{delete $attrs->{cols}}
48 : $source->result_class->_select_columns);
49 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
51 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
52 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
53 $attrs->{from} ||= [ { $alias => $source->from } ];
54 if (my $join = delete $attrs->{join}) {
55 foreach my $j (ref $join eq 'ARRAY'
56 ? (@{$join}) : ($join)) {
57 if (ref $j eq 'HASH') {
58 $seen{$_} = 1 foreach keys %$j;
63 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
65 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
66 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
67 push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias}))
71 $source->related_source($pre)->columns;
72 push(@{$attrs->{select}}, @pre);
73 push(@{$attrs->{as}}, @pre);
76 $attrs->{rows} ||= 10;
77 $attrs->{offset} ||= 0;
78 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
82 cond => $attrs->{where},
83 from => $attrs->{from},
85 page => delete $attrs->{page},
94 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
95 my $new_rs = $rs->search({ foo => 3 });
97 If you need to pass in additional attributes but no additional condition,
98 call it as ->search(undef, \%attrs);
100 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
107 #use Data::Dumper;warn Dumper(@_);
109 my $attrs = { %{$self->{attrs}} };
110 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
111 $attrs = { %$attrs, %{ pop(@_) } };
114 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
115 if (defined $where) {
116 $where = (defined $attrs->{where}
118 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
119 $where, $attrs->{where} ] }
121 $attrs->{where} = $where;
124 my $rs = (ref $self)->new($self->{source}, $attrs);
126 return (wantarray ? $rs->all : $rs);
129 =head2 search_literal
130 my @obj = $rs->search_literal($literal_where_cond, @bind);
131 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
133 Pass a literal chunk of SQL to be added to the conditional part of the
139 my ($self, $cond, @vals) = @_;
140 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
141 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
142 return $self->search(\$cond, $attrs);
145 =head2 find(@colvalues), find(\%cols)
147 Finds a row based on its primary key(s).
152 my ($self, @vals) = @_;
153 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
154 my @pk = $self->{source}->primary_columns;
155 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
156 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
159 if (ref $vals[0] eq 'HASH') {
161 } elsif (@pk == @vals) {
163 @{$query}{@pk} = @vals;
167 #warn Dumper($query);
168 # Useless -> disabled
169 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
170 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
171 # column names etc. Not sure what to do yet
172 return $self->search($query)->next;
175 =head2 search_related
177 $rs->search_related('relname', $cond?, $attrs?);
182 my ($self, $rel, @rest) = @_;
183 my $rel_obj = $self->{source}->relationship_info($rel);
184 $self->{source}->result_class->throw(
185 "No such relationship ${rel} in search_related")
187 my $rs = $self->search(undef, { join => $rel });
188 return $self->{source}->schema->resultset($rel_obj->{class}
199 Returns a storage-driven cursor to the given resultset.
205 my ($source, $attrs) = @{$self}{qw/source attrs/};
206 $attrs = { %$attrs };
207 return $self->{cursor}
208 ||= $source->storage->select($self->{from}, $attrs->{select},
209 $attrs->{where},$attrs);
214 Identical to search except defaults to 'LIKE' instead of '=' in condition
221 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
224 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
225 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
226 return $class->search($query, { %$attrs });
229 =head2 slice($first, $last)
231 Returns a subset of elements from the resultset.
236 my ($self, $min, $max) = @_;
237 my $attrs = { %{ $self->{attrs} || {} } };
238 $attrs->{offset} ||= 0;
239 $attrs->{offset} += $min;
240 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
241 my $slice = (ref $self)->new($self->{source}, $attrs);
242 return (wantarray ? $slice->all : $slice);
247 Returns the next element in the resultset (undef is there is none).
253 my @row = $self->cursor->next;
254 return unless (@row);
255 return $self->_construct_object(@row);
258 sub _construct_object {
259 my ($self, @row) = @_;
260 my @cols = @{ $self->{attrs}{as} };
261 #warn "@cols -> @row";
263 foreach my $col (@cols) {
264 if ($col =~ /([^\.]+)\.([^\.]+)/) {
265 $pre{$1}[0]{$2} = shift @row;
267 $me{$col} = shift @row;
270 my $new = $self->{source}->result_class->inflate_result(
271 $self->{source}, \%me, \%pre);
272 $new = $self->{attrs}{record_filter}->($new)
273 if exists $self->{attrs}{record_filter};
279 Performs an SQL C<COUNT> with the same query as the resultset was built
280 with to find the number of elements. If passed arguments, does a search
281 on the resultset and counts the results of that.
287 return $self->search(@_)->count if @_ && defined $_[0];
288 die "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
289 unless (defined $self->{count}) {
290 my $attrs = { %{ $self->{attrs} },
291 select => { 'count' => '*' },
293 # offset, order by and page are not needed to count. record_filter is cdbi
294 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
296 ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next;
298 return 0 unless $self->{count};
299 my $count = $self->{count};
300 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
301 $count = $self->{attrs}{rows} if
302 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
308 Calls search_literal with the passed arguments, then count.
312 sub count_literal { shift->search_literal(@_)->count; }
316 Returns all elements in the resultset. Called implictly if the resultset
317 is returned in list context.
323 return map { $self->_construct_object(@$_); }
329 Resets the resultset's cursor, so you can iterate through the elements again.
335 $self->cursor->reset;
341 Resets the resultset and returns the first element.
346 return $_[0]->reset->next;
349 =head2 update(\%values)
351 Sets the specified columns in the resultset to the supplied values
356 my ($self, $values) = @_;
357 die "Values for update must be a hash" unless ref $values eq 'HASH';
358 return $self->{source}->storage->update(
359 $self->{source}->from, $values, $self->{cond});
362 =head2 update_all(\%values)
364 Fetches all objects and updates them one at a time. ->update_all will run
365 cascade triggers, ->update will not.
370 my ($self, $values) = @_;
371 die "Values for update must be a hash" unless ref $values eq 'HASH';
372 foreach my $obj ($self->all) {
373 $obj->set_columns($values)->update;
380 Deletes the contents of the resultset from its result source.
386 $self->{source}->storage->delete($self->{source}->from, $self->{cond});
392 Fetches all objects and deletes them one at a time. ->delete_all will run
393 cascade triggers, ->delete will not.
399 $_->delete for $self->all;
405 Returns a L<Data::Page> object for the current resultset. Only makes
406 sense for queries with page turned on.
412 my $attrs = $self->{attrs};
413 die "Can't create pager for non-paged rs" unless $self->{page};
414 $attrs->{rows} ||= 10;
416 return $self->{pager} ||= Data::Page->new(
417 $self->{count}, $attrs->{rows}, $self->{page});
420 =head2 page($page_num)
422 Returns a new resultset for the specified page.
427 my ($self, $page) = @_;
428 my $attrs = { %{$self->{attrs}} };
429 $attrs->{page} = $page;
430 return (ref $self)->new($self->{source}, $attrs);
433 =head2 new_result(\%vals)
435 Creates a result in the resultset's result class
440 my ($self, $values) = @_;
441 $self->{source}->result_class->throw( "new_result needs a hash" )
442 unless (ref $values eq 'HASH');
443 $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" )
444 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
446 my $alias = $self->{attrs}{alias};
447 foreach my $key (keys %{$self->{cond}||{}}) {
448 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
450 my $obj = $self->{source}->result_class->new(\%new);
451 $obj->result_source($self->{source}) if $obj->can('result_source');
455 =head2 create(\%vals)
457 Inserts a record into the resultset and returns the object
459 Effectively a shortcut for ->new_result(\%vals)->insert
464 my ($self, $attrs) = @_;
465 $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH';
466 return $self->new_result($attrs)->insert;
469 =head2 find_or_create(\%vals)
471 $class->find_or_create({ key => $val, ... });
473 Searches for a record matching the search condition; if it doesn't find one,
474 creates one and returns that instead.
480 my $hash = ref $_[0] eq "HASH" ? shift: {@_};
481 my $exists = $self->find($hash);
482 return defined($exists) ? $exists : $self->create($hash);
487 The resultset takes various attributes that modify its behavior.
488 Here's an overview of them:
492 Which column(s) to order the results by. This is currently passed
493 through directly to SQL, so you can give e.g. C<foo DESC> for a
496 =head2 cols (arrayref)
498 Shortcut to request a particular set of columns to be retrieved - adds
499 'me.' onto the start of any column without a '.' in it and sets 'select'
500 from that, then auto-populates 'as' from 'select' as normal
502 =head2 select (arrayref)
504 Indicates which columns should be selected from the storage
508 Indicates column names for object inflation
512 Contains a list of relationships that should be joined for this query. Can also
513 contain a hash reference to refer to that relation's relations. So, if one column
514 in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
515 C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
516 If a foo contains many margles and you want to join those too, you can do
517 C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
518 related table as well, see C<prefetch> below.
522 Contains a list of relationships that should be fetched along with the main
523 query (when they are accessed afterwards they will have already been
524 "prefetched"). This is useful for when you know you will need the related
525 object(s), because it saves a query. Currently limited to prefetching
526 one relationship deep, so unlike C<join>, prefetch must be an arrayref.
530 This attribute can contain a arrayref of elements. Each element can be another
531 arrayref, to nest joins, or it can be a hash which represents the two sides
534 NOTE: Use this on your own risk. This allows you to shoot your foot off!
538 For a paged resultset, specifies which page to retrieve. Leave unset
539 for an unpaged resultset.
543 For a paged resultset, how many rows per page
545 =head2 group_by (listref)
547 A listref of columns to group by (note that 'count' doesn't work on grouped
550 group_by => [qw/ column1 column2 ... /]
554 Set to 1 to group by all columns