1 package DBIx::Class::ResultSet;
13 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
17 my $rs = MyApp::DB::Class->search(registered => 1);
18 my @rows = MyApp::DB::Class->search(foo => 'bar');
22 The resultset is also known as an iterator. It is responsible for handling
23 queries that may return an arbitrary number of rows, e.g. via C<search>
24 or a C<has_many> relationship.
28 =head2 new($source, \%$attrs)
30 The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
31 and an attribute hash (see below for more information on attributes). Does
32 not perform any queries -- these are executed as needed by the other methods.
38 return $class->new_result(@_) if ref $class;
39 my ($source, $attrs) = @_;
40 #use Data::Dumper; warn Dumper($attrs);
41 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
43 my $alias = ($attrs->{alias} ||= 'me');
44 if (!$attrs->{select}) {
45 my @cols = ($attrs->{cols}
46 ? @{delete $attrs->{cols}}
47 : $source->result_class->_select_columns);
48 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
50 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
51 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
52 $attrs->{from} ||= [ { $alias => $source->from } ];
53 if (my $join = delete $attrs->{join}) {
54 foreach my $j (ref $join eq 'ARRAY'
55 ? (@{$join}) : ($join)) {
56 if (ref $j eq 'HASH') {
57 $seen{$_} = 1 foreach keys %$j;
62 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
64 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
65 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
66 push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias}))
70 $source->related_source($pre)->columns;
71 push(@{$attrs->{select}}, @pre);
72 push(@{$attrs->{as}}, @pre);
75 $attrs->{rows} ||= 10;
76 $attrs->{offset} ||= 0;
77 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
81 cond => $attrs->{where},
82 from => $attrs->{from},
84 page => delete $attrs->{page},
93 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
94 my $new_rs = $rs->search({ foo => 3 });
96 If you need to pass in additional attributes but no additional condition,
97 call it as ->search(undef, \%attrs);
99 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
106 #use Data::Dumper;warn Dumper(@_);
108 my $attrs = { %{$self->{attrs}} };
109 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
110 $attrs = { %$attrs, %{ pop(@_) } };
113 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
114 if (defined $where) {
115 $where = (defined $attrs->{where}
117 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
118 $where, $attrs->{where} ] }
120 $attrs->{where} = $where;
123 my $rs = (ref $self)->new($self->{source}, $attrs);
125 return (wantarray ? $rs->all : $rs);
128 =head2 search_literal
129 my @obj = $rs->search_literal($literal_where_cond, @bind);
130 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
132 Pass a literal chunk of SQL to be added to the conditional part of the
138 my ($self, $cond, @vals) = @_;
139 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
140 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
141 return $self->search(\$cond, $attrs);
144 =head2 find(@colvalues), find(\%cols)
146 Finds a row based on its primary key(s).
151 my ($self, @vals) = @_;
152 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
153 my @pk = $self->{source}->primary_columns;
154 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
155 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
158 if (ref $vals[0] eq 'HASH') {
160 } elsif (@pk == @vals) {
162 @{$query}{@pk} = @vals;
166 #warn Dumper($query);
167 # Useless -> disabled
168 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
169 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
170 # column names etc. Not sure what to do yet
171 return $self->search($query)->next;
174 =head2 search_related
176 $rs->search_related('relname', $cond?, $attrs?);
181 my ($self, $rel, @rest) = @_;
182 my $rel_obj = $self->{source}->relationship_info($rel);
183 $self->{source}->result_class->throw(
184 "No such relationship ${rel} in search_related")
186 my $rs = $self->search(undef, { join => $rel });
187 return $self->{source}->schema->resultset($rel_obj->{class}
198 Returns a storage-driven cursor to the given resultset.
204 my ($source, $attrs) = @{$self}{qw/source attrs/};
205 $attrs = { %$attrs };
206 return $self->{cursor}
207 ||= $source->storage->select($self->{from}, $attrs->{select},
208 $attrs->{where},$attrs);
213 Identical to search except defaults to 'LIKE' instead of '=' in condition
220 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
223 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
224 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
225 return $class->search($query, { %$attrs });
228 =head2 slice($first, $last)
230 Returns a subset of elements from the resultset.
235 my ($self, $min, $max) = @_;
236 my $attrs = { %{ $self->{attrs} || {} } };
237 $attrs->{offset} ||= 0;
238 $attrs->{offset} += $min;
239 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
240 my $slice = (ref $self)->new($self->{source}, $attrs);
241 return (wantarray ? $slice->all : $slice);
246 Returns the next element in the resultset (undef is there is none).
252 my @row = $self->cursor->next;
253 return unless (@row);
254 return $self->_construct_object(@row);
257 sub _construct_object {
258 my ($self, @row) = @_;
259 my @cols = @{ $self->{attrs}{as} };
260 #warn "@cols -> @row";
262 foreach my $col (@cols) {
263 if ($col =~ /([^\.]+)\.([^\.]+)/) {
264 $pre{$1}[0]{$2} = shift @row;
266 $me{$col} = shift @row;
269 my $new = $self->{source}->result_class->inflate_result(
270 $self->{source}, \%me, \%pre);
271 $new = $self->{attrs}{record_filter}->($new)
272 if exists $self->{attrs}{record_filter};
278 Performs an SQL C<COUNT> with the same query as the resultset was built
279 with to find the number of elements. If passed arguments, does a search
280 on the resultset and counts the results of that.
286 return $self->search(@_)->count if @_ && defined $_[0];
287 die "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
288 unless (defined $self->{count}) {
289 my $attrs = { %{ $self->{attrs} },
290 select => { 'count' => '*' },
292 # offset, order by and page are not needed to count. record_filter is cdbi
293 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
295 ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next;
297 return 0 unless $self->{count};
298 my $count = $self->{count};
299 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
300 $count = $self->{attrs}{rows} if
301 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
307 Calls search_literal with the passed arguments, then count.
311 sub count_literal { shift->search_literal(@_)->count; }
315 Returns all elements in the resultset. Called implictly if the resultset
316 is returned in list context.
322 return map { $self->_construct_object(@$_); }
328 Resets the resultset's cursor, so you can iterate through the elements again.
334 $self->cursor->reset;
340 Resets the resultset and returns the first element.
345 return $_[0]->reset->next;
348 =head2 update(\%values)
350 Sets the specified columns in the resultset to the supplied values
355 my ($self, $values) = @_;
356 die "Values for update must be a hash" unless ref $values eq 'HASH';
357 return $self->{source}->storage->update(
358 $self->{source}->from, $values, $self->{cond});
361 =head2 update_all(\%values)
363 Fetches all objects and updates them one at a time. ->update_all will run
364 cascade triggers, ->update will not.
369 my ($self, $values) = @_;
370 die "Values for update must be a hash" unless ref $values eq 'HASH';
371 foreach my $obj ($self->all) {
372 $obj->set_columns($values)->update;
379 Deletes the contents of the resultset from its result source.
385 $self->{source}->storage->delete($self->{source}->from, $self->{cond});
391 Fetches all objects and deletes them one at a time. ->delete_all will run
392 cascade triggers, ->delete will not.
398 $_->delete for $self->all;
404 Returns a L<Data::Page> object for the current resultset. Only makes
405 sense for queries with page turned on.
411 my $attrs = $self->{attrs};
412 die "Can't create pager for non-paged rs" unless $self->{page};
413 $attrs->{rows} ||= 10;
415 return $self->{pager} ||= Data::Page->new(
416 $self->{count}, $attrs->{rows}, $self->{page});
419 =head2 page($page_num)
421 Returns a new resultset for the specified page.
426 my ($self, $page) = @_;
427 my $attrs = { %{$self->{attrs}} };
428 $attrs->{page} = $page;
429 return (ref $self)->new($self->{source}, $attrs);
432 =head2 new_result(\%vals)
434 Creates a result in the resultset's result class
439 my ($self, $values) = @_;
440 $self->{source}->result_class->throw( "new_result needs a hash" )
441 unless (ref $values eq 'HASH');
442 $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" )
443 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
445 my $alias = $self->{attrs}{alias};
446 foreach my $key (keys %{$self->{cond}||{}}) {
447 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
449 my $obj = $self->{source}->result_class->new(\%new);
450 $obj->result_source($self->{source}) if $obj->can('result_source');
454 =head2 create(\%vals)
456 Inserts a record into the resultset and returns the object
458 Effectively a shortcut for ->new_result(\%vals)->insert
463 my ($self, $attrs) = @_;
464 $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH';
465 return $self->new_result($attrs)->insert;
468 =head2 find_or_create(\%vals)
470 $class->find_or_create({ key => $val, ... });
472 Searches for a record matching the search condition; if it doesn't find one,
473 creates one and returns that instead.
479 my $hash = ref $_[0] eq "HASH" ? shift: {@_};
480 my $exists = $self->find($hash);
481 return defined($exists) ? $exists : $self->create($hash);
488 Just returns the resultset. Useful for Template Toolkit.
496 The resultset takes various attributes that modify its behavior.
497 Here's an overview of them:
501 Which column(s) to order the results by. This is currently passed
502 through directly to SQL, so you can give e.g. C<foo DESC> for a
505 =head2 cols (arrayref)
507 Shortcut to request a particular set of columns to be retrieved - adds
508 'me.' onto the start of any column without a '.' in it and sets 'select'
509 from that, then auto-populates 'as' from 'select' as normal
511 =head2 select (arrayref)
513 Indicates which columns should be selected from the storage
517 Indicates column names for object inflation
521 Contains a list of relationships that should be joined for this query. Can also
522 contain a hash reference to refer to that relation's relations. So, if one column
523 in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
524 C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
525 If a foo contains many margles and you want to join those too, you can do
526 C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
527 related table as well, see C<prefetch> below.
531 Contains a list of relationships that should be fetched along with the main
532 query (when they are accessed afterwards they will have already been
533 "prefetched"). This is useful for when you know you will need the related
534 object(s), because it saves a query. Currently limited to prefetching
535 one relationship deep, so unlike C<join>, prefetch must be an arrayref.
539 This attribute can contain a arrayref of elements. Each element can be another
540 arrayref, to nest joins, or it can be a hash which represents the two sides
543 NOTE: Use this on your own risk. This allows you to shoot your foot off!
547 For a paged resultset, specifies which page to retrieve. Leave unset
548 for an unpaged resultset.
552 For a paged resultset, how many rows per page
556 A list of columns to group by (note that 'count' doesn't work on grouped
561 Set to 1 to group by all columns