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