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