Added boolify to prevent if ($rs) blowing up on group_by'd resultsets
[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',
a910dc57 7 'bool' => sub { 1; },
89c0a5a2 8 fallback => 1;
3c5b25c5 9use Data::Page;
ea20d0fd 10use Storable;
89c0a5a2 11
ee38fa40 12=head1 NAME
13
bfab575a 14DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 15
bfab575a 16=head1 SYNOPSIS
ee38fa40 17
bfab575a 18my $rs = MyApp::DB::Class->search(registered => 1);
19my @rows = MyApp::DB::Class->search(foo => 'bar');
ee38fa40 20
21=head1 DESCRIPTION
22
bfab575a 23The resultset is also known as an iterator. It is responsible for handling
24queries that may return an arbitrary number of rows, e.g. via C<search>
25or a C<has_many> relationship.
ee38fa40 26
27=head1 METHODS
28
976f3686 29=head2 new($source, \%$attrs)
ee38fa40 30
976f3686 31The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
32and an attribute hash (see below for more information on attributes). Does
33not perform any queries -- these are executed as needed by the other methods.
ee38fa40 34
35=cut
36
89c0a5a2 37sub new {
fea3d045 38 my $class = shift;
f9db5527 39 return $class->new_result(@_) if ref $class;
fea3d045 40 my ($source, $attrs) = @_;
b98e75f6 41 #use Data::Dumper; warn Dumper($attrs);
ea20d0fd 42 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
c7ce65e6 43 my %seen;
6aeb9185 44 my $alias = ($attrs->{alias} ||= 'me');
976f3686 45 if (!$attrs->{select}) {
46 my @cols = ($attrs->{cols}
47 ? @{delete $attrs->{cols}}
48 : $source->result_class->_select_columns);
6aeb9185 49 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
976f3686 50 }
6aeb9185 51 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
976f3686 52 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
fea3d045 53 $attrs->{from} ||= [ { $alias => $source->from } ];
b52e9bf8 54 if (my $join = delete $attrs->{join}) {
55 foreach my $j (ref $join eq 'ARRAY'
56 ? (@{$join}) : ($join)) {
c7ce65e6 57 if (ref $j eq 'HASH') {
58 $seen{$_} = 1 foreach keys %$j;
59 } else {
60 $seen{$j} = 1;
61 }
62 }
8452e496 63 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
c7ce65e6 64 }
54540863 65 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
b52e9bf8 66 foreach my $pre (@{delete $attrs->{prefetch} || []}) {
8452e496 67 push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias}))
c7ce65e6 68 unless $seen{$pre};
976f3686 69 my @pre =
c7ce65e6 70 map { "$pre.$_" }
f9db5527 71 $source->related_source($pre)->columns;
976f3686 72 push(@{$attrs->{select}}, @pre);
73 push(@{$attrs->{as}}, @pre);
fef5d100 74 }
6aeb9185 75 if ($attrs->{page}) {
76 $attrs->{rows} ||= 10;
77 $attrs->{offset} ||= 0;
78 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
79 }
89c0a5a2 80 my $new = {
cda04c3a 81 source => $source,
89c0a5a2 82 cond => $attrs->{where},
0a3c5b43 83 from => $attrs->{from},
3c5b25c5 84 count => undef,
93b004d3 85 page => delete $attrs->{page},
3c5b25c5 86 pager => undef,
89c0a5a2 87 attrs => $attrs };
2f5911b2 88 bless ($new, $class);
9229f20a 89 return $new;
89c0a5a2 90}
91
bfab575a 92=head2 search
0a3c5b43 93
6009260a 94 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
95 my $new_rs = $rs->search({ foo => 3 });
96
97If you need to pass in additional attributes but no additional condition,
98call it as ->search(undef, \%attrs);
99
100 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
0a3c5b43 101
102=cut
103
104sub search {
105 my $self = shift;
106
6009260a 107 #use Data::Dumper;warn Dumper(@_);
108
0a3c5b43 109 my $attrs = { %{$self->{attrs}} };
110 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
6aeb9185 111 $attrs = { %$attrs, %{ pop(@_) } };
0a3c5b43 112 }
113
6aeb9185 114 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
0a3c5b43 115 if (defined $where) {
116 $where = (defined $attrs->{where}
ad3d2d7c 117 ? { '-and' =>
118 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
119 $where, $attrs->{where} ] }
0a3c5b43 120 : $where);
121 $attrs->{where} = $where;
122 }
123
fea3d045 124 my $rs = (ref $self)->new($self->{source}, $attrs);
0a3c5b43 125
126 return (wantarray ? $rs->all : $rs);
127}
128
bfab575a 129=head2 search_literal
6009260a 130 my @obj = $rs->search_literal($literal_where_cond, @bind);
131 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
132
133Pass a literal chunk of SQL to be added to the conditional part of the
134resultset
135
bfab575a 136=cut
137
6009260a 138sub search_literal {
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);
143}
0a3c5b43 144
716b3d29 145=head2 find(@colvalues), find(\%cols)
146
147Finds a row based on its primary key(s).
148
149=cut
150
151sub find {
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" )
157 unless @pk;
158 my $query;
159 if (ref $vals[0] eq 'HASH') {
160 $query = $vals[0];
161 } elsif (@pk == @vals) {
162 $query = {};
163 @{$query}{@pk} = @vals;
164 } else {
165 $query = {@vals};
166 }
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;
173}
174
b52e9bf8 175=head2 search_related
176
177 $rs->search_related('relname', $cond?, $attrs?);
178
179=cut
180
6aeb9185 181sub search_related {
182 my ($self, $rel, @rest) = @_;
f9db5527 183 my $rel_obj = $self->{source}->relationship_info($rel);
6aeb9185 184 $self->{source}->result_class->throw(
185 "No such relationship ${rel} in search_related")
186 unless $rel_obj;
6aeb9185 187 my $rs = $self->search(undef, { join => $rel });
ea20d0fd 188 return $self->{source}->schema->resultset($rel_obj->{class}
189 )->search( undef,
190 { %{$rs->{attrs}},
191 alias => $rel,
192 select => undef(),
193 as => undef() }
6aeb9185 194 )->search(@rest);
195}
b52e9bf8 196
bfab575a 197=head2 cursor
ee38fa40 198
bfab575a 199Returns a storage-driven cursor to the given resultset.
ee38fa40 200
201=cut
202
73f58123 203sub cursor {
204 my ($self) = @_;
2f5911b2 205 my ($source, $attrs) = @{$self}{qw/source attrs/};
6aeb9185 206 $attrs = { %$attrs };
73f58123 207 return $self->{cursor}
976f3686 208 ||= $source->storage->select($self->{from}, $attrs->{select},
73f58123 209 $attrs->{where},$attrs);
210}
211
bfab575a 212=head2 search_like
58a4bd18 213
214Identical to search except defaults to 'LIKE' instead of '=' in condition
215
216=cut
217
218sub search_like {
219 my $class = shift;
220 my $attrs = { };
221 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
222 $attrs = pop(@_);
223 }
224 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
225 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
226 return $class->search($query, { %$attrs });
227}
228
bfab575a 229=head2 slice($first, $last)
ee38fa40 230
bfab575a 231Returns a subset of elements from the resultset.
ee38fa40 232
233=cut
234
89c0a5a2 235sub slice {
236 my ($self, $min, $max) = @_;
237 my $attrs = { %{ $self->{attrs} || {} } };
6aeb9185 238 $attrs->{offset} ||= 0;
239 $attrs->{offset} += $min;
89c0a5a2 240 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
fea3d045 241 my $slice = (ref $self)->new($self->{source}, $attrs);
89c0a5a2 242 return (wantarray ? $slice->all : $slice);
243}
244
bfab575a 245=head2 next
ee38fa40 246
bfab575a 247Returns the next element in the resultset (undef is there is none).
ee38fa40 248
249=cut
250
89c0a5a2 251sub next {
252 my ($self) = @_;
73f58123 253 my @row = $self->cursor->next;
89c0a5a2 254 return unless (@row);
c7ce65e6 255 return $self->_construct_object(@row);
256}
257
258sub _construct_object {
259 my ($self, @row) = @_;
976f3686 260 my @cols = @{ $self->{attrs}{as} };
261 #warn "@cols -> @row";
b52e9bf8 262 my (%me, %pre);
263 foreach my $col (@cols) {
264 if ($col =~ /([^\.]+)\.([^\.]+)/) {
6aeb9185 265 $pre{$1}[0]{$2} = shift @row;
b52e9bf8 266 } else {
267 $me{$col} = shift @row;
c7ce65e6 268 }
c7ce65e6 269 }
c01ab172 270 my $new = $self->{source}->result_class->inflate_result(
271 $self->{source}, \%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' ] };
ea20d0fd 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/;
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
c01ab172 349=head2 update(\%values)
350
351Sets the specified columns in the resultset to the supplied values
352
353=cut
354
355sub update {
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});
360}
361
362=head2 update_all(\%values)
363
364Fetches all objects and updates them one at a time. ->update_all will run
365cascade triggers, ->update will not.
366
367=cut
368
369sub update_all {
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;
374 }
375 return 1;
376}
377
bfab575a 378=head2 delete
ee38fa40 379
c01ab172 380Deletes the contents of the resultset from its result source.
ee38fa40 381
382=cut
383
28927b50 384sub delete {
89c0a5a2 385 my ($self) = @_;
c01ab172 386 $self->{source}->storage->delete($self->{source}->from, $self->{cond});
89c0a5a2 387 return 1;
388}
389
c01ab172 390=head2 delete_all
391
392Fetches all objects and deletes them one at a time. ->delete_all will run
393cascade triggers, ->delete will not.
394
395=cut
396
397sub delete_all {
398 my ($self) = @_;
399 $_->delete for $self->all;
400 return 1;
401}
28927b50 402
bfab575a 403=head2 pager
ee38fa40 404
405Returns a L<Data::Page> object for the current resultset. Only makes
406sense for queries with page turned on.
407
408=cut
409
3c5b25c5 410sub pager {
411 my ($self) = @_;
412 my $attrs = $self->{attrs};
93b004d3 413 die "Can't create pager for non-paged rs" unless $self->{page};
6aeb9185 414 $attrs->{rows} ||= 10;
415 $self->count;
416 return $self->{pager} ||= Data::Page->new(
93b004d3 417 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 418}
419
bfab575a 420=head2 page($page_num)
ee38fa40 421
bfab575a 422Returns a new resultset for the specified page.
ee38fa40 423
424=cut
425
3c5b25c5 426sub page {
427 my ($self, $page) = @_;
6aeb9185 428 my $attrs = { %{$self->{attrs}} };
3c5b25c5 429 $attrs->{page} = $page;
fea3d045 430 return (ref $self)->new($self->{source}, $attrs);
431}
432
433=head2 new_result(\%vals)
434
435Creates a result in the resultset's result class
436
437=cut
438
439sub new_result {
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'));
445 my %new = %$values;
446 my $alias = $self->{attrs}{alias};
447 foreach my $key (keys %{$self->{cond}||{}}) {
448 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
449 }
097d3227 450 my $obj = $self->{source}->result_class->new(\%new);
451 $obj->result_source($self->{source}) if $obj->can('result_source');
452 $obj;
fea3d045 453}
454
455=head2 create(\%vals)
456
457Inserts a record into the resultset and returns the object
458
459Effectively a shortcut for ->new_result(\%vals)->insert
460
461=cut
462
463sub create {
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;
3c5b25c5 467}
468
c2b15ecc 469=head2 find_or_create(\%vals)
470
471 $class->find_or_create({ key => $val, ... });
472
473Searches for a record matching the search condition; if it doesn't find one,
474creates one and returns that instead.
475
476=cut
477
478sub find_or_create {
479 my $self = shift;
480 my $hash = ref $_[0] eq "HASH" ? shift: {@_};
481 my $exists = $self->find($hash);
482 return defined($exists) ? $exists : $self->create($hash);
483}
484
40dbc108 485=head1 ATTRIBUTES
076652e8 486
bfab575a 487The resultset takes various attributes that modify its behavior.
488Here's an overview of them:
489
490=head2 order_by
076652e8 491
bfab575a 492Which column(s) to order the results by. This is currently passed
493through directly to SQL, so you can give e.g. C<foo DESC> for a
494descending order.
076652e8 495
976f3686 496=head2 cols (arrayref)
497
498Shortcut 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'
500from that, then auto-populates 'as' from 'select' as normal
501
502=head2 select (arrayref)
503
504Indicates which columns should be selected from the storage
505
506=head2 as (arrayref)
076652e8 507
976f3686 508Indicates column names for object inflation
ee38fa40 509
bfab575a 510=head2 join
ee38fa40 511
bfab575a 512Contains a list of relationships that should be joined for this query. Can also
513contain a hash reference to refer to that relation's relations. So, if one column
514in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
515C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
516If a foo contains many margles and you want to join those too, you can do
517C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
518related table as well, see C<prefetch> below.
ee38fa40 519
bfab575a 520=head2 prefetch
ee38fa40 521
bfab575a 522Contains a list of relationships that should be fetched along with the main
523query (when they are accessed afterwards they will have already been
524"prefetched"). This is useful for when you know you will need the related
525object(s), because it saves a query. Currently limited to prefetching
526one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 527
bfab575a 528=head2 from
ee38fa40 529
bfab575a 530This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 531arrayref, to nest joins, or it can be a hash which represents the two sides
532of the join.
533
bfab575a 534NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 535
bfab575a 536=head2 page
076652e8 537
bfab575a 538For a paged resultset, specifies which page to retrieve. Leave unset
539for an unpaged resultset.
076652e8 540
bfab575a 541=head2 rows
076652e8 542
bfab575a 543For a paged resultset, how many rows per page
076652e8 544
675ce4a6 545=head2 group_by (listref)
54540863 546
675ce4a6 547A listref of columns to group by (note that 'count' doesn't work on grouped
54540863 548resultsets)
549
675ce4a6 550 group_by => [qw/ column1 column2 ... /]
551
54540863 552=head2 distinct
553
554Set to 1 to group by all columns
555
bfab575a 556=cut
076652e8 557
89c0a5a2 5581;