create now on resultset as well
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
CommitLineData
89c0a5a2 1package DBIx::Class::ResultSet;
2
3use strict;
4use warnings;
5use overload
6 '0+' => 'count',
7 fallback => 1;
3c5b25c5 8use Data::Page;
89c0a5a2 9
ee38fa40 10=head1 NAME
11
bfab575a 12DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 13
bfab575a 14=head1 SYNOPSIS
ee38fa40 15
bfab575a 16my $rs = MyApp::DB::Class->search(registered => 1);
17my @rows = MyApp::DB::Class->search(foo => 'bar');
ee38fa40 18
19=head1 DESCRIPTION
20
bfab575a 21The resultset is also known as an iterator. It is responsible for handling
22queries that may return an arbitrary number of rows, e.g. via C<search>
23or a C<has_many> relationship.
ee38fa40 24
25=head1 METHODS
26
976f3686 27=head2 new($source, \%$attrs)
ee38fa40 28
976f3686 29The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
30and an attribute hash (see below for more information on attributes). Does
31not perform any queries -- these are executed as needed by the other methods.
ee38fa40 32
33=cut
34
89c0a5a2 35sub new {
fea3d045 36 my $class = shift;
37 $class->new_result(@_) if ref $class;
38 my ($source, $attrs) = @_;
89c0a5a2 39 #use Data::Dumper; warn Dumper(@_);
89c0a5a2 40 $attrs = { %{ $attrs || {} } };
c7ce65e6 41 my %seen;
6aeb9185 42 my $alias = ($attrs->{alias} ||= 'me');
976f3686 43 if (!$attrs->{select}) {
44 my @cols = ($attrs->{cols}
45 ? @{delete $attrs->{cols}}
46 : $source->result_class->_select_columns);
6aeb9185 47 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
976f3686 48 }
6aeb9185 49 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
976f3686 50 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
fea3d045 51 $attrs->{from} ||= [ { $alias => $source->from } ];
b52e9bf8 52 if (my $join = delete $attrs->{join}) {
53 foreach my $j (ref $join eq 'ARRAY'
54 ? (@{$join}) : ($join)) {
c7ce65e6 55 if (ref $j eq 'HASH') {
56 $seen{$_} = 1 foreach keys %$j;
57 } else {
58 $seen{$j} = 1;
59 }
60 }
6aeb9185 61 push(@{$attrs->{from}}, $source->result_class->_resolve_join($join, $attrs->{alias}));
c7ce65e6 62 }
54540863 63 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
b52e9bf8 64 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
6aeb9185 65 push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, $attrs->{alias}))
c7ce65e6 66 unless $seen{$pre};
976f3686 67 my @pre =
c7ce65e6 68 map { "$pre.$_" }
976f3686 69 $source->result_class->_relationships->{$pre}->{class}->columns;
70 push(@{$attrs->{select}}, @pre);
71 push(@{$attrs->{as}}, @pre);
fef5d100 72 }
6aeb9185 73 if ($attrs->{page}) {
74 $attrs->{rows} ||= 10;
75 $attrs->{offset} ||= 0;
76 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
77 }
89c0a5a2 78 my $new = {
cda04c3a 79 source => $source,
89c0a5a2 80 cond => $attrs->{where},
0a3c5b43 81 from => $attrs->{from},
3c5b25c5 82 count => undef,
93b004d3 83 page => delete $attrs->{page},
3c5b25c5 84 pager => undef,
89c0a5a2 85 attrs => $attrs };
2f5911b2 86 bless ($new, $class);
9229f20a 87 return $new;
89c0a5a2 88}
89
bfab575a 90=head2 search
0a3c5b43 91
6009260a 92 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
93 my $new_rs = $rs->search({ foo => 3 });
94
95If you need to pass in additional attributes but no additional condition,
96call it as ->search(undef, \%attrs);
97
98 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
0a3c5b43 99
100=cut
101
102sub search {
103 my $self = shift;
104
6009260a 105 #use Data::Dumper;warn Dumper(@_);
106
0a3c5b43 107 my $attrs = { %{$self->{attrs}} };
108 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
6aeb9185 109 $attrs = { %$attrs, %{ pop(@_) } };
0a3c5b43 110 }
111
6aeb9185 112 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
0a3c5b43 113 if (defined $where) {
114 $where = (defined $attrs->{where}
115 ? { '-and' => [ $where, $attrs->{where} ] }
116 : $where);
117 $attrs->{where} = $where;
118 }
119
fea3d045 120 my $rs = (ref $self)->new($self->{source}, $attrs);
0a3c5b43 121
122 return (wantarray ? $rs->all : $rs);
123}
124
bfab575a 125=head2 search_literal
6009260a 126 my @obj = $rs->search_literal($literal_where_cond, @bind);
127 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
128
129Pass a literal chunk of SQL to be added to the conditional part of the
130resultset
131
bfab575a 132=cut
133
6009260a 134sub search_literal {
135 my ($self, $cond, @vals) = @_;
136 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
137 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
138 return $self->search(\$cond, $attrs);
139}
0a3c5b43 140
716b3d29 141=head2 find(@colvalues), find(\%cols)
142
143Finds a row based on its primary key(s).
144
145=cut
146
147sub find {
148 my ($self, @vals) = @_;
149 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
150 my @pk = $self->{source}->primary_columns;
151 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
152 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
153 unless @pk;
154 my $query;
155 if (ref $vals[0] eq 'HASH') {
156 $query = $vals[0];
157 } elsif (@pk == @vals) {
158 $query = {};
159 @{$query}{@pk} = @vals;
160 } else {
161 $query = {@vals};
162 }
163 #warn Dumper($query);
164 # Useless -> disabled
165 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
166 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
167 # column names etc. Not sure what to do yet
168 return $self->search($query)->next;
169}
170
b52e9bf8 171=head2 search_related
172
173 $rs->search_related('relname', $cond?, $attrs?);
174
175=cut
176
6aeb9185 177sub search_related {
178 my ($self, $rel, @rest) = @_;
179 my $rel_obj = $self->{source}->result_class->_relationships->{$rel};
180 $self->{source}->result_class->throw(
181 "No such relationship ${rel} in search_related")
182 unless $rel_obj;
183 my $r_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
184 my $source = $r_class->result_source;
185 $source = bless({ %{$source} }, ref $source || $source);
186 $source->storage($self->{source}->storage);
187 $source->result_class($r_class);
188 my $rs = $self->search(undef, { join => $rel });
189 #use Data::Dumper; warn Dumper($rs);
190 return $source->resultset_class->new(
191 $source, { %{$rs->{attrs}},
192 alias => $rel,
193 select => undef(),
194 as => undef() }
195 )->search(@rest);
196}
b52e9bf8 197
bfab575a 198=head2 cursor
ee38fa40 199
bfab575a 200Returns a storage-driven cursor to the given resultset.
ee38fa40 201
202=cut
203
73f58123 204sub cursor {
205 my ($self) = @_;
2f5911b2 206 my ($source, $attrs) = @{$self}{qw/source attrs/};
6aeb9185 207 $attrs = { %$attrs };
73f58123 208 return $self->{cursor}
976f3686 209 ||= $source->storage->select($self->{from}, $attrs->{select},
73f58123 210 $attrs->{where},$attrs);
211}
212
bfab575a 213=head2 search_like
58a4bd18 214
215Identical to search except defaults to 'LIKE' instead of '=' in condition
216
217=cut
218
219sub search_like {
220 my $class = shift;
221 my $attrs = { };
222 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
223 $attrs = pop(@_);
224 }
225 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
226 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
227 return $class->search($query, { %$attrs });
228}
229
bfab575a 230=head2 slice($first, $last)
ee38fa40 231
bfab575a 232Returns a subset of elements from the resultset.
ee38fa40 233
234=cut
235
89c0a5a2 236sub slice {
237 my ($self, $min, $max) = @_;
238 my $attrs = { %{ $self->{attrs} || {} } };
6aeb9185 239 $attrs->{offset} ||= 0;
240 $attrs->{offset} += $min;
89c0a5a2 241 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
fea3d045 242 my $slice = (ref $self)->new($self->{source}, $attrs);
89c0a5a2 243 return (wantarray ? $slice->all : $slice);
244}
245
bfab575a 246=head2 next
ee38fa40 247
bfab575a 248Returns the next element in the resultset (undef is there is none).
ee38fa40 249
250=cut
251
89c0a5a2 252sub next {
253 my ($self) = @_;
73f58123 254 my @row = $self->cursor->next;
89c0a5a2 255 return unless (@row);
c7ce65e6 256 return $self->_construct_object(@row);
257}
258
259sub _construct_object {
260 my ($self, @row) = @_;
976f3686 261 my @cols = @{ $self->{attrs}{as} };
262 #warn "@cols -> @row";
b52e9bf8 263 my (%me, %pre);
264 foreach my $col (@cols) {
265 if ($col =~ /([^\.]+)\.([^\.]+)/) {
6aeb9185 266 $pre{$1}[0]{$2} = shift @row;
b52e9bf8 267 } else {
268 $me{$col} = shift @row;
c7ce65e6 269 }
c7ce65e6 270 }
b52e9bf8 271 my $new = $self->{source}->result_class->inflate_result(\%me, \%pre);
33ce49d6 272 $new = $self->{attrs}{record_filter}->($new)
273 if exists $self->{attrs}{record_filter};
274 return $new;
89c0a5a2 275}
276
bfab575a 277=head2 count
ee38fa40 278
bfab575a 279Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 280with to find the number of elements. If passed arguments, does a search
281on the resultset and counts the results of that.
ee38fa40 282
283=cut
284
89c0a5a2 285sub count {
6009260a 286 my $self = shift;
287 return $self->search(@_)->count if @_ && defined $_[0];
54540863 288 die "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
6aeb9185 289 unless (defined $self->{count}) {
976f3686 290 my $attrs = { %{ $self->{attrs} },
54540863 291 select => { 'count' => '*' },
292 as => [ 'count' ] };
6aeb9185 293 # offset, order by and page are not needed to count
294 delete $attrs->{$_} for qw/rows offset order_by page pager/;
3c5b25c5 295
fea3d045 296 ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next;
3c5b25c5 297 }
298 return 0 unless $self->{count};
6aeb9185 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);
303 return $count;
89c0a5a2 304}
305
bfab575a 306=head2 count_literal
6009260a 307
bfab575a 308Calls search_literal with the passed arguments, then count.
6009260a 309
310=cut
311
312sub count_literal { shift->search_literal(@_)->count; }
313
bfab575a 314=head2 all
ee38fa40 315
bfab575a 316Returns all elements in the resultset. Called implictly if the resultset
317is returned in list context.
ee38fa40 318
319=cut
320
89c0a5a2 321sub all {
322 my ($self) = @_;
c7ce65e6 323 return map { $self->_construct_object(@$_); }
73f58123 324 $self->cursor->all;
89c0a5a2 325}
326
bfab575a 327=head2 reset
ee38fa40 328
bfab575a 329Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 330
331=cut
332
89c0a5a2 333sub reset {
334 my ($self) = @_;
73f58123 335 $self->cursor->reset;
89c0a5a2 336 return $self;
337}
338
bfab575a 339=head2 first
ee38fa40 340
bfab575a 341Resets the resultset and returns the first element.
ee38fa40 342
343=cut
344
89c0a5a2 345sub first {
346 return $_[0]->reset->next;
347}
348
bfab575a 349=head2 delete
ee38fa40 350
38659261 351Deletes all elements in the resultset.
ee38fa40 352
353=cut
354
28927b50 355sub delete {
89c0a5a2 356 my ($self) = @_;
357 $_->delete for $self->all;
358 return 1;
359}
360
28927b50 361*delete_all = \&delete; # Yeah, yeah, yeah ...
362
bfab575a 363=head2 pager
ee38fa40 364
365Returns a L<Data::Page> object for the current resultset. Only makes
366sense for queries with page turned on.
367
368=cut
369
3c5b25c5 370sub pager {
371 my ($self) = @_;
372 my $attrs = $self->{attrs};
93b004d3 373 die "Can't create pager for non-paged rs" unless $self->{page};
6aeb9185 374 $attrs->{rows} ||= 10;
375 $self->count;
376 return $self->{pager} ||= Data::Page->new(
93b004d3 377 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 378}
379
bfab575a 380=head2 page($page_num)
ee38fa40 381
bfab575a 382Returns a new resultset for the specified page.
ee38fa40 383
384=cut
385
3c5b25c5 386sub page {
387 my ($self, $page) = @_;
6aeb9185 388 my $attrs = { %{$self->{attrs}} };
3c5b25c5 389 $attrs->{page} = $page;
fea3d045 390 return (ref $self)->new($self->{source}, $attrs);
391}
392
393=head2 new_result(\%vals)
394
395Creates a result in the resultset's result class
396
397=cut
398
399sub new_result {
400 my ($self, $values) = @_;
401 $self->{source}->result_class->throw( "new_result needs a hash" )
402 unless (ref $values eq 'HASH');
403 $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" )
404 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
405 my %new = %$values;
406 my $alias = $self->{attrs}{alias};
407 foreach my $key (keys %{$self->{cond}||{}}) {
408 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
409 }
410 return $self->{source}->result_class->new(\%new);
411}
412
413=head2 create(\%vals)
414
415Inserts a record into the resultset and returns the object
416
417Effectively a shortcut for ->new_result(\%vals)->insert
418
419=cut
420
421sub create {
422 my ($self, $attrs) = @_;
423 $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH';
424 return $self->new_result($attrs)->insert;
3c5b25c5 425}
426
40dbc108 427=head1 ATTRIBUTES
076652e8 428
bfab575a 429The resultset takes various attributes that modify its behavior.
430Here's an overview of them:
431
432=head2 order_by
076652e8 433
bfab575a 434Which column(s) to order the results by. This is currently passed
435through directly to SQL, so you can give e.g. C<foo DESC> for a
436descending order.
076652e8 437
976f3686 438=head2 cols (arrayref)
439
440Shortcut to request a particular set of columns to be retrieved - adds
441'me.' onto the start of any column without a '.' in it and sets 'select'
442from that, then auto-populates 'as' from 'select' as normal
443
444=head2 select (arrayref)
445
446Indicates which columns should be selected from the storage
447
448=head2 as (arrayref)
076652e8 449
976f3686 450Indicates column names for object inflation
ee38fa40 451
bfab575a 452=head2 join
ee38fa40 453
bfab575a 454Contains a list of relationships that should be joined for this query. Can also
455contain a hash reference to refer to that relation's relations. So, if one column
456in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
457C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
458If a foo contains many margles and you want to join those too, you can do
459C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
460related table as well, see C<prefetch> below.
ee38fa40 461
bfab575a 462=head2 prefetch
ee38fa40 463
bfab575a 464Contains a list of relationships that should be fetched along with the main
465query (when they are accessed afterwards they will have already been
466"prefetched"). This is useful for when you know you will need the related
467object(s), because it saves a query. Currently limited to prefetching
468one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 469
bfab575a 470=head2 from
ee38fa40 471
bfab575a 472This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 473arrayref, to nest joins, or it can be a hash which represents the two sides
474of the join.
475
bfab575a 476NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 477
bfab575a 478=head2 page
076652e8 479
bfab575a 480For a paged resultset, specifies which page to retrieve. Leave unset
481for an unpaged resultset.
076652e8 482
bfab575a 483=head2 rows
076652e8 484
bfab575a 485For a paged resultset, how many rows per page
076652e8 486
54540863 487=head2 group_by
488
489A list of columns to group by (note that 'count' doesn't work on grouped
490resultsets)
491
492=head2 distinct
493
494Set to 1 to group by all columns
495
bfab575a 496=cut
076652e8 497
89c0a5a2 4981;