Nuked _select_columns, the last vestige of class-based evil
[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;
89c0a5a2 256 return unless (@row);
c7ce65e6 257 return $self->_construct_object(@row);
258}
259
260sub _construct_object {
261 my ($self, @row) = @_;
976f3686 262 my @cols = @{ $self->{attrs}{as} };
263 #warn "@cols -> @row";
b52e9bf8 264 my (%me, %pre);
265 foreach my $col (@cols) {
266 if ($col =~ /([^\.]+)\.([^\.]+)/) {
6aeb9185 267 $pre{$1}[0]{$2} = shift @row;
b52e9bf8 268 } else {
269 $me{$col} = shift @row;
c7ce65e6 270 }
c7ce65e6 271 }
c01ab172 272 my $new = $self->{source}->result_class->inflate_result(
273 $self->{source}, \%me, \%pre);
33ce49d6 274 $new = $self->{attrs}{record_filter}->($new)
275 if exists $self->{attrs}{record_filter};
276 return $new;
89c0a5a2 277}
278
bfab575a 279=head2 count
ee38fa40 280
bfab575a 281Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 282with to find the number of elements. If passed arguments, does a search
283on the resultset and counts the results of that.
ee38fa40 284
285=cut
286
89c0a5a2 287sub count {
6009260a 288 my $self = shift;
289 return $self->search(@_)->count if @_ && defined $_[0];
aa562407 290 croak "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
6aeb9185 291 unless (defined $self->{count}) {
976f3686 292 my $attrs = { %{ $self->{attrs} },
54540863 293 select => { 'count' => '*' },
294 as => [ 'count' ] };
ea20d0fd 295 # offset, order by and page are not needed to count. record_filter is cdbi
296 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
3c5b25c5 297
fea3d045 298 ($self->{count}) = (ref $self)->new($self->{source}, $attrs)->cursor->next;
3c5b25c5 299 }
300 return 0 unless $self->{count};
6aeb9185 301 my $count = $self->{count};
302 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
303 $count = $self->{attrs}{rows} if
304 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
305 return $count;
89c0a5a2 306}
307
bfab575a 308=head2 count_literal
6009260a 309
bfab575a 310Calls search_literal with the passed arguments, then count.
6009260a 311
312=cut
313
314sub count_literal { shift->search_literal(@_)->count; }
315
bfab575a 316=head2 all
ee38fa40 317
bfab575a 318Returns all elements in the resultset. Called implictly if the resultset
319is returned in list context.
ee38fa40 320
321=cut
322
89c0a5a2 323sub all {
324 my ($self) = @_;
c7ce65e6 325 return map { $self->_construct_object(@$_); }
73f58123 326 $self->cursor->all;
89c0a5a2 327}
328
bfab575a 329=head2 reset
ee38fa40 330
bfab575a 331Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 332
333=cut
334
89c0a5a2 335sub reset {
336 my ($self) = @_;
73f58123 337 $self->cursor->reset;
89c0a5a2 338 return $self;
339}
340
bfab575a 341=head2 first
ee38fa40 342
bfab575a 343Resets the resultset and returns the first element.
ee38fa40 344
345=cut
346
89c0a5a2 347sub first {
348 return $_[0]->reset->next;
349}
350
c01ab172 351=head2 update(\%values)
352
353Sets the specified columns in the resultset to the supplied values
354
355=cut
356
357sub update {
358 my ($self, $values) = @_;
aa562407 359 croak "Values for update must be a hash" unless ref $values eq 'HASH';
c01ab172 360 return $self->{source}->storage->update(
361 $self->{source}->from, $values, $self->{cond});
362}
363
364=head2 update_all(\%values)
365
366Fetches all objects and updates them one at a time. ->update_all will run
367cascade triggers, ->update will not.
368
369=cut
370
371sub update_all {
372 my ($self, $values) = @_;
aa562407 373 croak "Values for update must be a hash" unless ref $values eq 'HASH';
c01ab172 374 foreach my $obj ($self->all) {
375 $obj->set_columns($values)->update;
376 }
377 return 1;
378}
379
bfab575a 380=head2 delete
ee38fa40 381
c01ab172 382Deletes the contents of the resultset from its result source.
ee38fa40 383
384=cut
385
28927b50 386sub delete {
89c0a5a2 387 my ($self) = @_;
c01ab172 388 $self->{source}->storage->delete($self->{source}->from, $self->{cond});
89c0a5a2 389 return 1;
390}
391
c01ab172 392=head2 delete_all
393
394Fetches all objects and deletes them one at a time. ->delete_all will run
395cascade triggers, ->delete will not.
396
397=cut
398
399sub delete_all {
400 my ($self) = @_;
401 $_->delete for $self->all;
402 return 1;
403}
28927b50 404
bfab575a 405=head2 pager
ee38fa40 406
407Returns a L<Data::Page> object for the current resultset. Only makes
408sense for queries with page turned on.
409
410=cut
411
3c5b25c5 412sub pager {
413 my ($self) = @_;
414 my $attrs = $self->{attrs};
aa562407 415 croak "Can't create pager for non-paged rs" unless $self->{page};
6aeb9185 416 $attrs->{rows} ||= 10;
417 $self->count;
418 return $self->{pager} ||= Data::Page->new(
93b004d3 419 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 420}
421
bfab575a 422=head2 page($page_num)
ee38fa40 423
bfab575a 424Returns a new resultset for the specified page.
ee38fa40 425
426=cut
427
3c5b25c5 428sub page {
429 my ($self, $page) = @_;
6aeb9185 430 my $attrs = { %{$self->{attrs}} };
3c5b25c5 431 $attrs->{page} = $page;
fea3d045 432 return (ref $self)->new($self->{source}, $attrs);
433}
434
435=head2 new_result(\%vals)
436
437Creates a result in the resultset's result class
438
439=cut
440
441sub new_result {
442 my ($self, $values) = @_;
443 $self->{source}->result_class->throw( "new_result needs a hash" )
444 unless (ref $values eq 'HASH');
445 $self->{source}->result_class->throw( "Can't abstract implicit construct, condition not a hash" )
446 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
447 my %new = %$values;
448 my $alias = $self->{attrs}{alias};
449 foreach my $key (keys %{$self->{cond}||{}}) {
450 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
451 }
097d3227 452 my $obj = $self->{source}->result_class->new(\%new);
453 $obj->result_source($self->{source}) if $obj->can('result_source');
454 $obj;
fea3d045 455}
456
457=head2 create(\%vals)
458
459Inserts a record into the resultset and returns the object
460
461Effectively a shortcut for ->new_result(\%vals)->insert
462
463=cut
464
465sub create {
466 my ($self, $attrs) = @_;
467 $self->{source}->result_class->throw( "create needs a hashref" ) unless ref $attrs eq 'HASH';
468 return $self->new_result($attrs)->insert;
3c5b25c5 469}
470
c2b15ecc 471=head2 find_or_create(\%vals)
472
473 $class->find_or_create({ key => $val, ... });
474
475Searches for a record matching the search condition; if it doesn't find one,
476creates one and returns that instead.
477
478=cut
479
480sub find_or_create {
481 my $self = shift;
482 my $hash = ref $_[0] eq "HASH" ? shift: {@_};
483 my $exists = $self->find($hash);
484 return defined($exists) ? $exists : $self->create($hash);
485}
486
40dbc108 487=head1 ATTRIBUTES
076652e8 488
bfab575a 489The resultset takes various attributes that modify its behavior.
490Here's an overview of them:
491
492=head2 order_by
076652e8 493
bfab575a 494Which column(s) to order the results by. This is currently passed
495through directly to SQL, so you can give e.g. C<foo DESC> for a
496descending order.
076652e8 497
976f3686 498=head2 cols (arrayref)
499
500Shortcut to request a particular set of columns to be retrieved - adds
501'me.' onto the start of any column without a '.' in it and sets 'select'
502from that, then auto-populates 'as' from 'select' as normal
503
504=head2 select (arrayref)
505
506Indicates which columns should be selected from the storage
507
508=head2 as (arrayref)
076652e8 509
976f3686 510Indicates column names for object inflation
ee38fa40 511
bfab575a 512=head2 join
ee38fa40 513
bfab575a 514Contains a list of relationships that should be joined for this query. Can also
515contain a hash reference to refer to that relation's relations. So, if one column
516in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
517C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
518If a foo contains many margles and you want to join those too, you can do
519C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
520related table as well, see C<prefetch> below.
ee38fa40 521
bfab575a 522=head2 prefetch
ee38fa40 523
bfab575a 524Contains a list of relationships that should be fetched along with the main
525query (when they are accessed afterwards they will have already been
526"prefetched"). This is useful for when you know you will need the related
527object(s), because it saves a query. Currently limited to prefetching
528one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 529
bfab575a 530=head2 from
ee38fa40 531
bfab575a 532This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 533arrayref, to nest joins, or it can be a hash which represents the two sides
534of the join.
535
bfab575a 536NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 537
bfab575a 538=head2 page
076652e8 539
bfab575a 540For a paged resultset, specifies which page to retrieve. Leave unset
541for an unpaged resultset.
076652e8 542
bfab575a 543=head2 rows
076652e8 544
bfab575a 545For a paged resultset, how many rows per page
076652e8 546
675ce4a6 547=head2 group_by (listref)
54540863 548
675ce4a6 549A listref of columns to group by (note that 'count' doesn't work on grouped
54540863 550resultsets)
551
675ce4a6 552 group_by => [qw/ column1 column2 ... /]
553
54540863 554=head2 distinct
555
556Set to 1 to group by all columns
557
bfab575a 558=cut
076652e8 559
89c0a5a2 5601;