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