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