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