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