find_related now does search_related->find
[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
716b3d29 140=head2 find(@colvalues), find(\%cols)
141
142Finds a row based on its primary key(s).
143
144=cut
145
146sub find {
147 my ($self, @vals) = @_;
148 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
149 my @pk = $self->{source}->primary_columns;
150 #use Data::Dumper; warn Dumper($attrs, @vals, @pk);
151 $self->{source}->result_class->throw( "Can't find unless primary columns are defined" )
152 unless @pk;
153 my $query;
154 if (ref $vals[0] eq 'HASH') {
155 $query = $vals[0];
156 } elsif (@pk == @vals) {
157 $query = {};
158 @{$query}{@pk} = @vals;
159 } else {
160 $query = {@vals};
161 }
162 #warn Dumper($query);
163 # Useless -> disabled
164 #$self->{source}->result_class->throw( "Can't find unless all primary keys are specified" )
165 # unless (keys %$query >= @pk); # If we check 'em we run afoul of uc/lc
166 # column names etc. Not sure what to do yet
167 return $self->search($query)->next;
168}
169
b52e9bf8 170=head2 search_related
171
172 $rs->search_related('relname', $cond?, $attrs?);
173
174=cut
175
6aeb9185 176sub search_related {
177 my ($self, $rel, @rest) = @_;
178 my $rel_obj = $self->{source}->result_class->_relationships->{$rel};
179 $self->{source}->result_class->throw(
180 "No such relationship ${rel} in search_related")
181 unless $rel_obj;
182 my $r_class = $self->{source}->result_class->resolve_class($rel_obj->{class});
183 my $source = $r_class->result_source;
184 $source = bless({ %{$source} }, ref $source || $source);
185 $source->storage($self->{source}->storage);
186 $source->result_class($r_class);
187 my $rs = $self->search(undef, { join => $rel });
188 #use Data::Dumper; warn Dumper($rs);
189 return $source->resultset_class->new(
190 $source, { %{$rs->{attrs}},
191 alias => $rel,
192 select => undef(),
193 as => undef() }
194 )->search(@rest);
195}
b52e9bf8 196
bfab575a 197=head2 cursor
ee38fa40 198
bfab575a 199Returns a storage-driven cursor to the given resultset.
ee38fa40 200
201=cut
202
73f58123 203sub cursor {
204 my ($self) = @_;
2f5911b2 205 my ($source, $attrs) = @{$self}{qw/source attrs/};
6aeb9185 206 $attrs = { %$attrs };
73f58123 207 return $self->{cursor}
976f3686 208 ||= $source->storage->select($self->{from}, $attrs->{select},
73f58123 209 $attrs->{where},$attrs);
210}
211
bfab575a 212=head2 search_like
58a4bd18 213
214Identical to search except defaults to 'LIKE' instead of '=' in condition
215
216=cut
217
218sub search_like {
219 my $class = shift;
220 my $attrs = { };
221 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
222 $attrs = pop(@_);
223 }
224 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
225 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
226 return $class->search($query, { %$attrs });
227}
228
bfab575a 229=head2 slice($first, $last)
ee38fa40 230
bfab575a 231Returns a subset of elements from the resultset.
ee38fa40 232
233=cut
234
89c0a5a2 235sub slice {
236 my ($self, $min, $max) = @_;
237 my $attrs = { %{ $self->{attrs} || {} } };
6aeb9185 238 $attrs->{offset} ||= 0;
239 $attrs->{offset} += $min;
89c0a5a2 240 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
2f5911b2 241 my $slice = $self->new($self->{source}, $attrs);
89c0a5a2 242 return (wantarray ? $slice->all : $slice);
243}
244
bfab575a 245=head2 next
ee38fa40 246
bfab575a 247Returns the next element in the resultset (undef is there is none).
ee38fa40 248
249=cut
250
89c0a5a2 251sub next {
252 my ($self) = @_;
73f58123 253 my @row = $self->cursor->next;
89c0a5a2 254 return unless (@row);
c7ce65e6 255 return $self->_construct_object(@row);
256}
257
258sub _construct_object {
259 my ($self, @row) = @_;
976f3686 260 my @cols = @{ $self->{attrs}{as} };
261 #warn "@cols -> @row";
b52e9bf8 262 my (%me, %pre);
263 foreach my $col (@cols) {
264 if ($col =~ /([^\.]+)\.([^\.]+)/) {
6aeb9185 265 $pre{$1}[0]{$2} = shift @row;
b52e9bf8 266 } else {
267 $me{$col} = shift @row;
c7ce65e6 268 }
c7ce65e6 269 }
b52e9bf8 270 my $new = $self->{source}->result_class->inflate_result(\%me, \%pre);
33ce49d6 271 $new = $self->{attrs}{record_filter}->($new)
272 if exists $self->{attrs}{record_filter};
273 return $new;
89c0a5a2 274}
275
bfab575a 276=head2 count
ee38fa40 277
bfab575a 278Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 279with to find the number of elements. If passed arguments, does a search
280on the resultset and counts the results of that.
ee38fa40 281
282=cut
283
89c0a5a2 284sub count {
6009260a 285 my $self = shift;
286 return $self->search(@_)->count if @_ && defined $_[0];
54540863 287 die "Unable to ->count with a GROUP BY" if defined $self->{attrs}{group_by};
6aeb9185 288 unless (defined $self->{count}) {
976f3686 289 my $attrs = { %{ $self->{attrs} },
54540863 290 select => { 'count' => '*' },
291 as => [ 'count' ] };
6aeb9185 292 # offset, order by and page are not needed to count
293 delete $attrs->{$_} for qw/rows offset order_by page pager/;
3c5b25c5 294
6aeb9185 295 ($self->{count}) = $self->new($self->{source}, $attrs)->cursor->next;
3c5b25c5 296 }
297 return 0 unless $self->{count};
6aeb9185 298 my $count = $self->{count};
299 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
300 $count = $self->{attrs}{rows} if
301 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
302 return $count;
89c0a5a2 303}
304
bfab575a 305=head2 count_literal
6009260a 306
bfab575a 307Calls search_literal with the passed arguments, then count.
6009260a 308
309=cut
310
311sub count_literal { shift->search_literal(@_)->count; }
312
bfab575a 313=head2 all
ee38fa40 314
bfab575a 315Returns all elements in the resultset. Called implictly if the resultset
316is returned in list context.
ee38fa40 317
318=cut
319
89c0a5a2 320sub all {
321 my ($self) = @_;
c7ce65e6 322 return map { $self->_construct_object(@$_); }
73f58123 323 $self->cursor->all;
89c0a5a2 324}
325
bfab575a 326=head2 reset
ee38fa40 327
bfab575a 328Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 329
330=cut
331
89c0a5a2 332sub reset {
333 my ($self) = @_;
73f58123 334 $self->cursor->reset;
89c0a5a2 335 return $self;
336}
337
bfab575a 338=head2 first
ee38fa40 339
bfab575a 340Resets the resultset and returns the first element.
ee38fa40 341
342=cut
343
89c0a5a2 344sub first {
345 return $_[0]->reset->next;
346}
347
bfab575a 348=head2 delete
ee38fa40 349
38659261 350Deletes all elements in the resultset.
ee38fa40 351
352=cut
353
28927b50 354sub delete {
89c0a5a2 355 my ($self) = @_;
356 $_->delete for $self->all;
357 return 1;
358}
359
28927b50 360*delete_all = \&delete; # Yeah, yeah, yeah ...
361
bfab575a 362=head2 pager
ee38fa40 363
364Returns a L<Data::Page> object for the current resultset. Only makes
365sense for queries with page turned on.
366
367=cut
368
3c5b25c5 369sub pager {
370 my ($self) = @_;
371 my $attrs = $self->{attrs};
93b004d3 372 die "Can't create pager for non-paged rs" unless $self->{page};
6aeb9185 373 $attrs->{rows} ||= 10;
374 $self->count;
375 return $self->{pager} ||= Data::Page->new(
93b004d3 376 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 377}
378
bfab575a 379=head2 page($page_num)
ee38fa40 380
bfab575a 381Returns a new resultset for the specified page.
ee38fa40 382
383=cut
384
3c5b25c5 385sub page {
386 my ($self, $page) = @_;
6aeb9185 387 my $attrs = { %{$self->{attrs}} };
3c5b25c5 388 $attrs->{page} = $page;
2f5911b2 389 return $self->new($self->{source}, $attrs);
3c5b25c5 390}
391
40dbc108 392=head1 ATTRIBUTES
076652e8 393
bfab575a 394The resultset takes various attributes that modify its behavior.
395Here's an overview of them:
396
397=head2 order_by
076652e8 398
bfab575a 399Which column(s) to order the results by. This is currently passed
400through directly to SQL, so you can give e.g. C<foo DESC> for a
401descending order.
076652e8 402
976f3686 403=head2 cols (arrayref)
404
405Shortcut to request a particular set of columns to be retrieved - adds
406'me.' onto the start of any column without a '.' in it and sets 'select'
407from that, then auto-populates 'as' from 'select' as normal
408
409=head2 select (arrayref)
410
411Indicates which columns should be selected from the storage
412
413=head2 as (arrayref)
076652e8 414
976f3686 415Indicates column names for object inflation
ee38fa40 416
bfab575a 417=head2 join
ee38fa40 418
bfab575a 419Contains a list of relationships that should be joined for this query. Can also
420contain a hash reference to refer to that relation's relations. So, if one column
421in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
422C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
423If a foo contains many margles and you want to join those too, you can do
424C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
425related table as well, see C<prefetch> below.
ee38fa40 426
bfab575a 427=head2 prefetch
ee38fa40 428
bfab575a 429Contains a list of relationships that should be fetched along with the main
430query (when they are accessed afterwards they will have already been
431"prefetched"). This is useful for when you know you will need the related
432object(s), because it saves a query. Currently limited to prefetching
433one relationship deep, so unlike C<join>, prefetch must be an arrayref.
ee38fa40 434
bfab575a 435=head2 from
ee38fa40 436
bfab575a 437This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 438arrayref, to nest joins, or it can be a hash which represents the two sides
439of the join.
440
bfab575a 441NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 442
bfab575a 443=head2 page
076652e8 444
bfab575a 445For a paged resultset, specifies which page to retrieve. Leave unset
446for an unpaged resultset.
076652e8 447
bfab575a 448=head2 rows
076652e8 449
bfab575a 450For a paged resultset, how many rows per page
076652e8 451
54540863 452=head2 group_by
453
454A list of columns to group by (note that 'count' doesn't work on grouped
455resultsets)
456
457=head2 distinct
458
459Set to 1 to group by all columns
460
bfab575a 461=cut
076652e8 462
89c0a5a2 4631;