1 package DBIx::Class::ResultSet;
12 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
16 my $rs = MyApp::DB::Class->search(registered => 1);
17 my @rows = MyApp::DB::Class->search(foo => 'bar');
21 The resultset is also known as an iterator. It is responsible for handling
22 queries that may return an arbitrary number of rows, e.g. via C<search>
23 or a C<has_many> relationship.
27 =head2 new($source, \%$attrs)
29 The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
30 and an attribute hash (see below for more information on attributes). Does
31 not perform any queries -- these are executed as needed by the other methods.
36 my ($class, $source, $attrs) = @_;
37 #use Data::Dumper; warn Dumper(@_);
38 $class = ref $class if ref $class;
39 $attrs = { %{ $attrs || {} } };
41 my $alias = ($attrs->{alias} ||= 'me');
42 if (!$attrs->{select}) {
43 my @cols = ($attrs->{cols}
44 ? @{delete $attrs->{cols}}
45 : $source->result_class->_select_columns);
46 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
48 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
49 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
50 $attrs->{from} ||= [ { $alias => $source->name } ];
51 if (my $join = delete $attrs->{join}) {
52 foreach my $j (ref $join eq 'ARRAY'
53 ? (@{$join}) : ($join)) {
54 if (ref $j eq 'HASH') {
55 $seen{$_} = 1 foreach keys %$j;
60 push(@{$attrs->{from}}, $source->result_class->_resolve_join($join, $attrs->{alias}));
62 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
63 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
64 push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, $attrs->{alias}))
68 $source->result_class->_relationships->{$pre}->{class}->columns;
69 push(@{$attrs->{select}}, @pre);
70 push(@{$attrs->{as}}, @pre);
73 $attrs->{rows} ||= 10;
74 $attrs->{offset} ||= 0;
75 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
79 cond => $attrs->{where},
80 from => $attrs->{from},
82 page => delete $attrs->{page},
91 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
92 my $new_rs = $rs->search({ foo => 3 });
94 If you need to pass in additional attributes but no additional condition,
95 call it as ->search(undef, \%attrs);
97 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
104 #use Data::Dumper;warn Dumper(@_);
106 my $attrs = { %{$self->{attrs}} };
107 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
108 $attrs = { %$attrs, %{ pop(@_) } };
111 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
112 if (defined $where) {
113 $where = (defined $attrs->{where}
114 ? { '-and' => [ $where, $attrs->{where} ] }
116 $attrs->{where} = $where;
119 my $rs = $self->new($self->{source}, $attrs);
121 return (wantarray ? $rs->all : $rs);
124 =head2 search_literal
125 my @obj = $rs->search_literal($literal_where_cond, @bind);
126 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
128 Pass a literal chunk of SQL to be added to the conditional part of the
134 my ($self, $cond, @vals) = @_;
135 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
136 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
137 return $self->search(\$cond, $attrs);
140 =head2 find(@colvalues), find(\%cols)
142 Finds a row based on its primary key(s).
147 my ($self, @vals) = @_;
148 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
149 my @pk = $self->{source}->primary_columns;
150 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
151 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
154 if (ref $vals[0] eq 'HASH') {
156 } elsif (@pk == @vals) {
158 @{$query}{@pk} = @vals;
162 #warn Dumper($query);
163 # Useless -> disabled
164 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
165 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
166 # column names etc. Not sure what to do yet
167 return $self->search($query)->next;
170 =head2 search_related
172 $rs->search_related('relname', $cond?, $attrs?);
177 my ($self, $rel, @rest) = @_;
178 my $rel_obj = $self->{source}->result_class->_relationships->{$rel};
179 $self->{source}->result_class->throw(
180 "No such relationship ${rel} in search_related")
182 my $r_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
183 my $source = $r_class->result_source;
184 $source = bless({ %{$source} }, ref $source || $source);
185 $source->storage($self->{source}->storage);
186 $source->result_class($r_class);
187 my $rs = $self->search(undef, { join => $rel });
188 #use Data::Dumper; warn Dumper($rs);
189 return $source->resultset_class->new(
190 $source, { %{$rs->{attrs}},
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 = $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(\%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
293 delete $attrs->{$_} for qw/rows offset order_by page pager/;
295 ($self->{count}) = $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;
350 Deletes all elements in the resultset.
356 $_->delete for $self->all;
360 *delete_all = \&delete; # Yeah, yeah, yeah ...
364 Returns a L<Data::Page> object for the current resultset. Only makes
365 sense for queries with page turned on.
371 my $attrs = $self->{attrs};
372 die "Can't create pager for non-paged rs" unless $self->{page};
373 $attrs->{rows} ||= 10;
375 return $self->{pager} ||= Data::Page->new(
376 $self->{count}, $attrs->{rows}, $self->{page});
379 =head2 page($page_num)
381 Returns a new resultset for the specified page.
386 my ($self, $page) = @_;
387 my $attrs = { %{$self->{attrs}} };
388 $attrs->{page} = $page;
389 return $self->new($self->{source}, $attrs);
394 The resultset takes various attributes that modify its behavior.
395 Here's an overview of them:
399 Which column(s) to order the results by. This is currently passed
400 through directly to SQL, so you can give e.g. C<foo DESC> for a
403 =head2 cols (arrayref)
405 Shortcut to request a particular set of columns to be retrieved - adds
406 'me.' onto the start of any column without a '.' in it and sets 'select'
407 from that, then auto-populates 'as' from 'select' as normal
409 =head2 select (arrayref)
411 Indicates which columns should be selected from the storage
415 Indicates column names for object inflation
419 Contains a list of relationships that should be joined for this query. Can also
420 contain a hash reference to refer to that relation's relations. So, if one column
421 in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
422 C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
423 If a foo contains many margles and you want to join those too, you can do
424 C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
425 related table as well, see C<prefetch> below.
429 Contains a list of relationships that should be fetched along with the main
430 query (when they are accessed afterwards they will have already been
431 "prefetched"). This is useful for when you know you will need the related
432 object(s), because it saves a query. Currently limited to prefetching
433 one relationship deep, so unlike C<join>, prefetch must be an arrayref.
437 This attribute can contain a arrayref of elements. Each element can be another
438 arrayref, to nest joins, or it can be a hash which represents the two sides
441 NOTE: Use this on your own risk. This allows you to shoot your foot off!
445 For a paged resultset, specifies which page to retrieve. Leave unset
446 for an unpaged resultset.
450 For a paged resultset, how many rows per page
454 A list of columns to group by (note that 'count' doesn't work on grouped
459 Set to 1 to group by all columns