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