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