Merge 'trunk' into 'DBIx-Class-resultset'
[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;
89c0a5a2 9
ee38fa40 10=head1 NAME
11
bfab575a 12DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 13
bfab575a 14=head1 SYNOPSIS
ee38fa40 15
bfab575a 16my $rs = MyApp::DB::Class->search(registered => 1);
17my @rows = MyApp::DB::Class->search(foo => 'bar');
ee38fa40 18
19=head1 DESCRIPTION
20
bfab575a 21The resultset is also known as an iterator. It is responsible for handling
22queries that may return an arbitrary number of rows, e.g. via C<search>
23or a C<has_many> relationship.
ee38fa40 24
25=head1 METHODS
26
976f3686 27=head2 new($source, \%$attrs)
ee38fa40 28
976f3686 29The resultset constructor. Takes a source object (usually a DBIx::Class::Table)
30and an attribute hash (see below for more information on attributes). Does
31not perform any queries -- these are executed as needed by the other methods.
ee38fa40 32
33=cut
34
89c0a5a2 35sub new {
cda04c3a 36 my ($class, $source, $attrs) = @_;
89c0a5a2 37 #use Data::Dumper; warn Dumper(@_);
2f5911b2 38 $class = ref $class if ref $class;
89c0a5a2 39 $attrs = { %{ $attrs || {} } };
c7ce65e6 40 my %seen;
976f3686 41 if (!$attrs->{select}) {
42 my @cols = ($attrs->{cols}
43 ? @{delete $attrs->{cols}}
44 : $source->result_class->_select_columns);
45 $attrs->{select} = [ map { m/\./ ? $_ : "me.$_" } @cols ];
46 }
47 $attrs->{as} ||= [ map { m/^me\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
48 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
cda04c3a 49 $attrs->{from} ||= [ { 'me' => $source->name } ];
fef5d100 50 if ($attrs->{join}) {
c7ce65e6 51 foreach my $j (ref $attrs->{join} eq 'ARRAY'
52 ? (@{$attrs->{join}}) : ($attrs->{join})) {
53 if (ref $j eq 'HASH') {
54 $seen{$_} = 1 foreach keys %$j;
55 } else {
56 $seen{$j} = 1;
57 }
58 }
cda04c3a 59 push(@{$attrs->{from}}, $source->result_class->_resolve_join($attrs->{join}, 'me'));
c7ce65e6 60 }
61 foreach my $pre (@{$attrs->{prefetch} || []}) {
cda04c3a 62 push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, 'me'))
c7ce65e6 63 unless $seen{$pre};
976f3686 64 my @pre =
c7ce65e6 65 map { "$pre.$_" }
976f3686 66 $source->result_class->_relationships->{$pre}->{class}->columns;
67 push(@{$attrs->{select}}, @pre);
68 push(@{$attrs->{as}}, @pre);
fef5d100 69 }
89c0a5a2 70 my $new = {
cda04c3a 71 source => $source,
89c0a5a2 72 cond => $attrs->{where},
0a3c5b43 73 from => $attrs->{from},
3c5b25c5 74 count => undef,
75 pager => undef,
89c0a5a2 76 attrs => $attrs };
2f5911b2 77 bless ($new, $class);
976f3686 78 $new->pager if $attrs->{page};
9229f20a 79 return $new;
89c0a5a2 80}
81
bfab575a 82=head2 search
0a3c5b43 83
6009260a 84 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
85 my $new_rs = $rs->search({ foo => 3 });
86
87If you need to pass in additional attributes but no additional condition,
88call it as ->search(undef, \%attrs);
89
90 my @all = $class->search({}, { cols => [qw/foo bar/] }); # "SELECT foo, bar FROM $class_table"
0a3c5b43 91
92=cut
93
94sub search {
95 my $self = shift;
96
6009260a 97 #use Data::Dumper;warn Dumper(@_);
98
0a3c5b43 99 my $attrs = { %{$self->{attrs}} };
100 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
101 $attrs = { %{ pop(@_) } };
102 }
103
6009260a 104 my $where = ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_});
0a3c5b43 105 if (defined $where) {
106 $where = (defined $attrs->{where}
107 ? { '-and' => [ $where, $attrs->{where} ] }
108 : $where);
109 $attrs->{where} = $where;
110 }
111
112 my $rs = $self->new($self->{source}, $attrs);
113
114 return (wantarray ? $rs->all : $rs);
115}
116
bfab575a 117=head2 search_literal
6009260a 118 my @obj = $rs->search_literal($literal_where_cond, @bind);
119 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
120
121Pass a literal chunk of SQL to be added to the conditional part of the
122resultset
123
bfab575a 124=cut
125
6009260a 126sub search_literal {
127 my ($self, $cond, @vals) = @_;
128 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
129 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
130 return $self->search(\$cond, $attrs);
131}
0a3c5b43 132
bfab575a 133=head2 cursor
ee38fa40 134
bfab575a 135Returns a storage-driven cursor to the given resultset.
ee38fa40 136
137=cut
138
73f58123 139sub cursor {
140 my ($self) = @_;
2f5911b2 141 my ($source, $attrs) = @{$self}{qw/source attrs/};
3c5b25c5 142 if ($attrs->{page}) {
143 $attrs->{rows} = $self->pager->entries_per_page;
144 $attrs->{offset} = $self->pager->skipped;
145 }
73f58123 146 return $self->{cursor}
976f3686 147 ||= $source->storage->select($self->{from}, $attrs->{select},
73f58123 148 $attrs->{where},$attrs);
149}
150
bfab575a 151=head2 search_like
58a4bd18 152
153Identical to search except defaults to 'LIKE' instead of '=' in condition
154
155=cut
156
157sub search_like {
158 my $class = shift;
159 my $attrs = { };
160 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
161 $attrs = pop(@_);
162 }
163 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
164 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
165 return $class->search($query, { %$attrs });
166}
167
bfab575a 168=head2 slice($first, $last)
ee38fa40 169
bfab575a 170Returns a subset of elements from the resultset.
ee38fa40 171
172=cut
173
89c0a5a2 174sub slice {
175 my ($self, $min, $max) = @_;
176 my $attrs = { %{ $self->{attrs} || {} } };
cda04c3a 177 $self->{source}->result_class->throw("Can't slice without where") unless $attrs->{where};
89c0a5a2 178 $attrs->{offset} = $min;
179 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
2f5911b2 180 my $slice = $self->new($self->{source}, $attrs);
89c0a5a2 181 return (wantarray ? $slice->all : $slice);
182}
183
bfab575a 184=head2 next
ee38fa40 185
bfab575a 186Returns the next element in the resultset (undef is there is none).
ee38fa40 187
188=cut
189
89c0a5a2 190sub next {
191 my ($self) = @_;
73f58123 192 my @row = $self->cursor->next;
89c0a5a2 193 return unless (@row);
c7ce65e6 194 return $self->_construct_object(@row);
195}
196
197sub _construct_object {
198 my ($self, @row) = @_;
976f3686 199 my @cols = @{ $self->{attrs}{as} };
200 #warn "@cols -> @row";
3cbddeb1 201 @cols = grep { /\(/ or ! /\./ } @cols;
33ce49d6 202 my $new;
c7ce65e6 203 unless ($self->{attrs}{prefetch}) {
cda04c3a 204 $new = $self->{source}->result_class->_row_to_object(\@cols, \@row);
c7ce65e6 205 } else {
206 my @main = splice(@row, 0, scalar @cols);
cda04c3a 207 $new = $self->{source}->result_class->_row_to_object(\@cols, \@main);
c7ce65e6 208 PRE: foreach my $pre (@{$self->{attrs}{prefetch}}) {
cda04c3a 209 my $rel_obj = $self->{source}->result_class->_relationships->{$pre};
210 my $pre_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
dd417d06 211 my @pre_cols = $pre_class->_select_columns;
c7ce65e6 212 my @vals = splice(@row, 0, scalar @pre_cols);
dd417d06 213 my $fetched = $pre_class->_row_to_object(\@pre_cols, \@vals);
cda04c3a 214 $self->{source}->result_class->throw("No accessor for prefetched $pre")
c7ce65e6 215 unless defined $rel_obj->{attrs}{accessor};
216 if ($rel_obj->{attrs}{accessor} eq 'single') {
217 foreach my $pri ($rel_obj->{class}->primary_columns) {
7cd300ea 218 unless (defined $fetched->get_column($pri)) {
219 undef $fetched;
220 last;
221 }
c7ce65e6 222 }
223 $new->{_relationship_data}{$pre} = $fetched;
224 } elsif ($rel_obj->{attrs}{accessor} eq 'filter') {
225 $new->{_inflated_column}{$pre} = $fetched;
226 } else {
cda04c3a 227 $self->{source}->result_class->throw("Don't know how to store prefetched $pre");
c7ce65e6 228 }
229 }
c7ce65e6 230 }
33ce49d6 231 $new = $self->{attrs}{record_filter}->($new)
232 if exists $self->{attrs}{record_filter};
233 return $new;
89c0a5a2 234}
235
bfab575a 236=head2 count
ee38fa40 237
bfab575a 238Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 239with to find the number of elements. If passed arguments, does a search
240on the resultset and counts the results of that.
ee38fa40 241
242=cut
243
89c0a5a2 244sub count {
6009260a 245 my $self = shift;
246 return $self->search(@_)->count if @_ && defined $_[0];
3c5b25c5 247 unless ($self->{count}) {
976f3686 248 my $attrs = { %{ $self->{attrs} },
249 select => [ 'COUNT(*)' ], as => [ 'count' ] };
250 # offset and order by are not needed to count, page, join and prefetch
251 # will get in the way (add themselves to from again ...)
252 delete $attrs->{$_} for qw/offset order_by page join prefetch/;
3c5b25c5 253
254 my @cols = 'COUNT(*)';
976f3686 255 ($self->{count}) = $self->search(undef, $attrs)->cursor->next;
3c5b25c5 256 }
257 return 0 unless $self->{count};
9229f20a 258 return $self->{pager}->entries_on_this_page if ($self->{pager});
976f3686 259 return ( $self->{attrs}->{rows} && $self->{attrs}->{rows} < $self->{count} )
260 ? $self->{attrs}->{rows}
3c5b25c5 261 : $self->{count};
89c0a5a2 262}
263
bfab575a 264=head2 count_literal
6009260a 265
bfab575a 266Calls search_literal with the passed arguments, then count.
6009260a 267
268=cut
269
270sub count_literal { shift->search_literal(@_)->count; }
271
bfab575a 272=head2 all
ee38fa40 273
bfab575a 274Returns all elements in the resultset. Called implictly if the resultset
275is returned in list context.
ee38fa40 276
277=cut
278
89c0a5a2 279sub all {
280 my ($self) = @_;
c7ce65e6 281 return map { $self->_construct_object(@$_); }
73f58123 282 $self->cursor->all;
89c0a5a2 283}
284
bfab575a 285=head2 reset
ee38fa40 286
bfab575a 287Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 288
289=cut
290
89c0a5a2 291sub reset {
292 my ($self) = @_;
73f58123 293 $self->cursor->reset;
89c0a5a2 294 return $self;
295}
296
bfab575a 297=head2 first
ee38fa40 298
bfab575a 299Resets the resultset and returns the first element.
ee38fa40 300
301=cut
302
89c0a5a2 303sub first {
304 return $_[0]->reset->next;
305}
306
bfab575a 307=head2 delete
ee38fa40 308
38659261 309Deletes all elements in the resultset.
ee38fa40 310
311=cut
312
28927b50 313sub delete {
89c0a5a2 314 my ($self) = @_;
315 $_->delete for $self->all;
316 return 1;
317}
318
28927b50 319*delete_all = \&delete; # Yeah, yeah, yeah ...
320
bfab575a 321=head2 pager
ee38fa40 322
323Returns a L<Data::Page> object for the current resultset. Only makes
324sense for queries with page turned on.
325
326=cut
327
3c5b25c5 328sub pager {
329 my ($self) = @_;
330 my $attrs = $self->{attrs};
331 delete $attrs->{offset};
332 my $rows_per_page = delete $attrs->{rows} || 10;
333 $self->{pager} ||= Data::Page->new(
334 $self->count, $rows_per_page, $attrs->{page} || 1);
335 $attrs->{rows} = $rows_per_page;
336 return $self->{pager};
337}
338
bfab575a 339=head2 page($page_num)
ee38fa40 340
bfab575a 341Returns a new resultset for the specified page.
ee38fa40 342
343=cut
344
3c5b25c5 345sub page {
346 my ($self, $page) = @_;
347 my $attrs = $self->{attrs};
348 $attrs->{page} = $page;
2f5911b2 349 return $self->new($self->{source}, $attrs);
3c5b25c5 350}
351
076652e8 352=head1 Attributes
353
bfab575a 354The resultset takes various attributes that modify its behavior.
355Here's an overview of them:
356
357=head2 order_by
076652e8 358
bfab575a 359Which column(s) to order the results by. This is currently passed
360through directly to SQL, so you can give e.g. C<foo DESC> for a
361descending order.
076652e8 362
976f3686 363=head2 cols (arrayref)
364
365Shortcut to request a particular set of columns to be retrieved - adds
366'me.' onto the start of any column without a '.' in it and sets 'select'
367from that, then auto-populates 'as' from 'select' as normal
368
369=head2 select (arrayref)
370
371Indicates which columns should be selected from the storage
372
373=head2 as (arrayref)
076652e8 374
976f3686 375Indicates column names for object inflation
ee38fa40 376
bfab575a 377=head2 join
ee38fa40 378
bfab575a 379Contains a list of relationships that should be joined for this query. Can also
380contain a hash reference to refer to that relation's relations. So, if one column
381in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
382C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
383If a foo contains many margles and you want to join those too, you can do
384C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
385related table as well, see C<prefetch> below.
ee38fa40 386
bfab575a 387=head2 prefetch
ee38fa40 388
bfab575a 389Contains a list of relationships that should be fetched along with the main
390query (when they are accessed afterwards they will have already been
391"prefetched"). This is useful for when you know you will need the related
392object(s), because it saves a query. Currently limited to prefetching
393one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 394
bfab575a 395=head2 from
ee38fa40 396
bfab575a 397This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 398arrayref, to nest joins, or it can be a hash which represents the two sides
399of the join.
400
bfab575a 401NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 402
bfab575a 403=head2 page
076652e8 404
bfab575a 405For a paged resultset, specifies which page to retrieve. Leave unset
406for an unpaged resultset.
076652e8 407
bfab575a 408=head2 rows
076652e8 409
bfab575a 410For a paged resultset, how many rows per page
076652e8 411
bfab575a 412=cut
076652e8 413
89c0a5a2 4141;