nested has_many prefetch + tests
[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',
a910dc57 7 'bool' => sub { 1; },
89c0a5a2 8 fallback => 1;
3c5b25c5 9use Data::Page;
ea20d0fd 10use Storable;
89c0a5a2 11
701da8c4 12use base qw/DBIx::Class/;
13__PACKAGE__->load_components(qw/AccessorGroup/);
14__PACKAGE__->mk_group_accessors('simple' => 'result_source');
15
ee38fa40 16=head1 NAME
17
bfab575a 18DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 19
bfab575a 20=head1 SYNOPSIS
ee38fa40 21
a33df5d4 22 my $rs = $schema->resultset('User')->search(registered => 1);
23 my @rows = $schema->resultset('Foo')->search(bar => 'baz');
ee38fa40 24
25=head1 DESCRIPTION
26
bfab575a 27The resultset is also known as an iterator. It is responsible for handling
a33df5d4 28queries that may return an arbitrary number of rows, e.g. via L</search>
bfab575a 29or a C<has_many> relationship.
ee38fa40 30
a33df5d4 31In the examples below, the following table classes are used:
32
33 package MyApp::Schema::Artist;
34 use base qw/DBIx::Class/;
35 __PACKAGE__->table('artist');
36 __PACKAGE__->add_columns(qw/artistid name/);
37 __PACKAGE__->set_primary_key('artistid');
38 __PACKAGE__->has_many(cds => 'MyApp::Schema::CD');
39 1;
40
41 package MyApp::Schema::CD;
42 use base qw/DBIx::Class/;
43 __PACKAGE__->table('artist');
44 __PACKAGE__->add_columns(qw/cdid artist title year/);
45 __PACKAGE__->set_primary_key('cdid');
46 __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
47 1;
48
ee38fa40 49=head1 METHODS
50
87c4e602 51=head2 new
52
53=head3 Arguments: ($source, \%$attrs)
ee38fa40 54
a33df5d4 55The resultset constructor. Takes a source object (usually a
80c90f5d 56L<DBIx::Class::ResultSourceProxy::Table>) and an attribute hash (see L</ATRRIBUTES>
a33df5d4 57below). Does not perform any queries -- these are executed as needed by the
58other methods.
59
60Generally you won't need to construct a resultset manually. You'll
61automatically get one from e.g. a L</search> called in scalar context:
62
63 my $rs = $schema->resultset('CD')->search({ title => '100th Window' });
ee38fa40 64
65=cut
66
89c0a5a2 67sub new {
fea3d045 68 my $class = shift;
f9db5527 69 return $class->new_result(@_) if ref $class;
fea3d045 70 my ($source, $attrs) = @_;
b98e75f6 71 #use Data::Dumper; warn Dumper($attrs);
ea20d0fd 72 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
c7ce65e6 73 my %seen;
6aeb9185 74 my $alias = ($attrs->{alias} ||= 'me');
a9433341 75 if ($attrs->{cols} || !$attrs->{select}) {
76 delete $attrs->{as} if $attrs->{cols};
976f3686 77 my @cols = ($attrs->{cols}
78 ? @{delete $attrs->{cols}}
a9433341 79 : $source->columns);
6aeb9185 80 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
976f3686 81 }
6aeb9185 82 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
5ac6a044 83 if (my $include = delete $attrs->{include_columns}) {
84 push(@{$attrs->{select}}, @$include);
85 push(@{$attrs->{as}}, map { m/([^\.]+)$/; $1; } @$include);
86 }
976f3686 87 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
fea3d045 88 $attrs->{from} ||= [ { $alias => $source->from } ];
8fab5eef 89 $attrs->{seen_join} ||= {};
b52e9bf8 90 if (my $join = delete $attrs->{join}) {
91 foreach my $j (ref $join eq 'ARRAY'
92 ? (@{$join}) : ($join)) {
c7ce65e6 93 if (ref $j eq 'HASH') {
94 $seen{$_} = 1 foreach keys %$j;
95 } else {
96 $seen{$j} = 1;
97 }
98 }
8fab5eef 99 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}, $attrs->{seen_join}));
c7ce65e6 100 }
54540863 101 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
b3e8ac9b 102
103 if (my $prefetch = delete $attrs->{prefetch}) {
104 foreach my $p (ref $prefetch eq 'ARRAY'
105 ? (@{$prefetch}) : ($prefetch)) {
106 if( ref $p eq 'HASH' ) {
107 foreach my $key (keys %$p) {
108 push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
109 unless $seen{$key};
110 }
111 }
112 else {
113 push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
114 unless $seen{$p};
115 }
489709af 116 my @prefetch = $source->resolve_prefetch($p, $attrs->{alias});
b3e8ac9b 117 #die Dumper \@cols;
489709af 118 push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
119 push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
b3e8ac9b 120 }
fef5d100 121 }
b3e8ac9b 122
6aeb9185 123 if ($attrs->{page}) {
124 $attrs->{rows} ||= 10;
125 $attrs->{offset} ||= 0;
126 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
127 }
89c0a5a2 128 my $new = {
701da8c4 129 result_source => $source,
89c0a5a2 130 cond => $attrs->{where},
0a3c5b43 131 from => $attrs->{from},
3c5b25c5 132 count => undef,
93b004d3 133 page => delete $attrs->{page},
3c5b25c5 134 pager => undef,
89c0a5a2 135 attrs => $attrs };
2f5911b2 136 bless ($new, $class);
9229f20a 137 return $new;
89c0a5a2 138}
139
bfab575a 140=head2 search
0a3c5b43 141
87f0da6a 142 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
143 my $new_rs = $rs->search({ foo => 3 });
144
6009260a 145If you need to pass in additional attributes but no additional condition,
a33df5d4 146call it as C<search({}, \%attrs);>.
87f0da6a 147
a33df5d4 148 # "SELECT foo, bar FROM $class_table"
149 my @all = $class->search({}, { cols => [qw/foo bar/] });
0a3c5b43 150
151=cut
152
153sub search {
154 my $self = shift;
155
6009260a 156 #use Data::Dumper;warn Dumper(@_);
ff7bb7a1 157 my $rs;
158 if( @_ ) {
159
160 my $attrs = { %{$self->{attrs}} };
161 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
162 $attrs = { %$attrs, %{ pop(@_) } };
163 }
6009260a 164
ff7bb7a1 165 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
166 if (defined $where) {
167 $where = (defined $attrs->{where}
ad3d2d7c 168 ? { '-and' =>
169 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
170 $where, $attrs->{where} ] }
0a3c5b43 171 : $where);
ff7bb7a1 172 $attrs->{where} = $where;
173 }
0a3c5b43 174
ff7bb7a1 175 $rs = (ref $self)->new($self->result_source, $attrs);
176 }
177 else {
178 $rs = $self;
179 $rs->reset();
180 }
0a3c5b43 181 return (wantarray ? $rs->all : $rs);
182}
183
87f0da6a 184=head2 search_literal
185
6009260a 186 my @obj = $rs->search_literal($literal_where_cond, @bind);
187 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
188
189Pass a literal chunk of SQL to be added to the conditional part of the
87f0da6a 190resultset.
6009260a 191
bfab575a 192=cut
193
6009260a 194sub search_literal {
195 my ($self, $cond, @vals) = @_;
196 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
197 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
198 return $self->search(\$cond, $attrs);
199}
0a3c5b43 200
87c4e602 201=head2 find
202
203=head3 Arguments: (@colvalues) | (\%cols, \%attrs?)
87f0da6a 204
205Finds a row based on its primary key or unique constraint. For example:
206
87f0da6a 207 my $cd = $schema->resultset('CD')->find(5);
208
209Also takes an optional C<key> attribute, to search by a specific key or unique
210constraint. For example:
211
212 my $cd = $schema->resultset('CD')->find_or_create(
213 {
214 artist => 'Massive Attack',
215 title => 'Mezzanine',
216 },
217 { key => 'artist_title' }
218 );
219
a33df5d4 220See also L</find_or_create> and L</update_or_create>.
221
87f0da6a 222=cut
716b3d29 223
224sub find {
225 my ($self, @vals) = @_;
226 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
87f0da6a 227
701da8c4 228 my @cols = $self->result_source->primary_columns;
87f0da6a 229 if (exists $attrs->{key}) {
701da8c4 230 my %uniq = $self->result_source->unique_constraints;
87f0da6a 231 $self->( "Unknown key " . $attrs->{key} . " on " . $self->name )
232 unless exists $uniq{$attrs->{key}};
233 @cols = @{ $uniq{$attrs->{key}} };
234 }
235 #use Data::Dumper; warn Dumper($attrs, @vals, @cols);
701da8c4 236 $self->throw_exception( "Can't find unless a primary key or unique constraint is defined" )
87f0da6a 237 unless @cols;
238
716b3d29 239 my $query;
240 if (ref $vals[0] eq 'HASH') {
01bc091e 241 $query = { %{$vals[0]} };
87f0da6a 242 } elsif (@cols == @vals) {
716b3d29 243 $query = {};
87f0da6a 244 @{$query}{@cols} = @vals;
716b3d29 245 } else {
246 $query = {@vals};
247 }
01bc091e 248 foreach (keys %$query) {
249 next if m/\./;
250 $query->{$self->{attrs}{alias}.'.'.$_} = delete $query->{$_};
251 }
716b3d29 252 #warn Dumper($query);
a04ab285 253 return (keys %$attrs
254 ? $self->search($query,$attrs)->single
255 : $self->single($query));
716b3d29 256}
257
b52e9bf8 258=head2 search_related
259
260 $rs->search_related('relname', $cond?, $attrs?);
261
a33df5d4 262Search the specified relationship. Optionally specify a condition for matching
263records.
264
b52e9bf8 265=cut
266
6aeb9185 267sub search_related {
64acc2bc 268 return shift->related_resultset(shift)->search(@_);
6aeb9185 269}
b52e9bf8 270
bfab575a 271=head2 cursor
ee38fa40 272
bfab575a 273Returns a storage-driven cursor to the given resultset.
ee38fa40 274
275=cut
276
73f58123 277sub cursor {
278 my ($self) = @_;
701da8c4 279 my ($attrs) = $self->{attrs};
6aeb9185 280 $attrs = { %$attrs };
73f58123 281 return $self->{cursor}
701da8c4 282 ||= $self->result_source->storage->select($self->{from}, $attrs->{select},
73f58123 283 $attrs->{where},$attrs);
284}
285
a04ab285 286=head2 single
287
288Inflates the first result without creating a cursor
289
290=cut
291
292sub single {
293 my ($self, $extra) = @_;
294 my ($attrs) = $self->{attrs};
295 $attrs = { %$attrs };
296 if ($extra) {
297 if (defined $attrs->{where}) {
298 $attrs->{where} = {
299 '-and'
300 => [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
301 delete $attrs->{where}, $extra ]
302 };
303 } else {
304 $attrs->{where} = $extra;
305 }
306 }
307 my @data = $self->result_source->storage->select_single(
308 $self->{from}, $attrs->{select},
309 $attrs->{where},$attrs);
310 return (@data ? $self->_construct_object(@data) : ());
311}
312
313
87f0da6a 314=head2 search_like
315
a33df5d4 316Perform a search, but use C<LIKE> instead of equality as the condition. Note
317that this is simply a convenience method; you most likely want to use
318L</search> with specific operators.
319
320For more information, see L<DBIx::Class::Manual::Cookbook>.
87f0da6a 321
322=cut
58a4bd18 323
324sub search_like {
325 my $class = shift;
326 my $attrs = { };
327 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
328 $attrs = pop(@_);
329 }
330 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
331 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
332 return $class->search($query, { %$attrs });
333}
334
87c4e602 335=head2 slice
336
337=head3 Arguments: ($first, $last)
ee38fa40 338
bfab575a 339Returns a subset of elements from the resultset.
ee38fa40 340
341=cut
342
89c0a5a2 343sub slice {
344 my ($self, $min, $max) = @_;
345 my $attrs = { %{ $self->{attrs} || {} } };
6aeb9185 346 $attrs->{offset} ||= 0;
347 $attrs->{offset} += $min;
89c0a5a2 348 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
701da8c4 349 my $slice = (ref $self)->new($self->result_source, $attrs);
89c0a5a2 350 return (wantarray ? $slice->all : $slice);
351}
352
87f0da6a 353=head2 next
ee38fa40 354
a33df5d4 355Returns the next element in the resultset (C<undef> is there is none).
356
357Can be used to efficiently iterate over records in the resultset:
358
359 my $rs = $schema->resultset('CD')->search({});
360 while (my $cd = $rs->next) {
361 print $cd->title;
362 }
ee38fa40 363
364=cut
365
89c0a5a2 366sub next {
367 my ($self) = @_;
64acc2bc 368 my $cache = $self->get_cache;
369 if( @$cache ) {
370 $self->{all_cache_position} ||= 0;
371 my $obj = $cache->[$self->{all_cache_position}];
372 $self->{all_cache_position}++;
373 return $obj;
374 }
73f58123 375 my @row = $self->cursor->next;
a953d8d9 376# warn Dumper(\@row); use Data::Dumper;
89c0a5a2 377 return unless (@row);
c7ce65e6 378 return $self->_construct_object(@row);
379}
380
381sub _construct_object {
382 my ($self, @row) = @_;
64acc2bc 383 my @row_orig = @row; # copy @row for key comparison later, because @row will change
b3e8ac9b 384 my @as = @{ $self->{attrs}{as} };
f9cc31dd 385#use Data::Dumper; warn Dumper \@as;
976f3686 386 #warn "@cols -> @row";
b3e8ac9b 387 my $info = [ {}, {} ];
388 foreach my $as (@as) {
64acc2bc 389 my $rs = $self;
b3e8ac9b 390 my $target = $info;
391 my @parts = split(/\./, $as);
392 my $col = pop(@parts);
393 foreach my $p (@parts) {
394 $target = $target->[1]->{$p} ||= [];
64acc2bc 395
f9cc31dd 396 $rs = $rs->related_resultset($p) if $rs->{attrs}->{cache};
c7ce65e6 397 }
f9cc31dd 398
64acc2bc 399 $target->[0]->{$col} = shift @row
400 if ref($target->[0]) ne 'ARRAY'; # arrayref is pre-inflated objects, do not overwrite
c7ce65e6 401 }
b3e8ac9b 402 #use Data::Dumper; warn Dumper(\@as, $info);
701da8c4 403 my $new = $self->result_source->result_class->inflate_result(
404 $self->result_source, @$info);
33ce49d6 405 $new = $self->{attrs}{record_filter}->($new)
406 if exists $self->{attrs}{record_filter};
f9cc31dd 407
408 if( $self->{attrs}->{cache} ) {
409 while( my( $rel, $rs ) = each( %{$self->{related_resultsets}} ) ) {
410 $rs->all;
411 #warn "$rel:", @{$rs->get_cache};
412 }
413 $self->build_rr( $self, $new );
414 }
415
33ce49d6 416 return $new;
89c0a5a2 417}
f9cc31dd 418
419sub build_rr {
420 # build related resultsets for supplied object
421 my ( $self, $context, $obj ) = @_;
422
423 my $re = qr/^\w+\./;
424 while( my ($rel, $rs) = each( %{$context->{related_resultsets}} ) ) {
425 #warn "context:", $context->result_source->name, ", rel:$rel, rs:", $rs->result_source->name;
426 my @objs = ();
427 my $map = {};
428 my $cond = $context->result_source->relationship_info($rel)->{cond};
429 keys %$cond;
430 while( my( $rel_key, $pk ) = each(%$cond) ) {
431 $rel_key =~ s/$re//;
432 $pk =~ s/$re//;
433 $map->{$rel_key} = $pk;
434 }
435
436 $rs->reset();
437 while( my $rel_obj = $rs->next ) {
438 while( my( $rel_key, $pk ) = each(%$map) ) {
439 if( $rel_obj->get_column($rel_key) eq $obj->get_column($pk) ) {
440 push @objs, $rel_obj;
441 }
442 }
443 }
444
445 my $rel_rs = $obj->related_resultset($rel);
446 $rel_rs->{attrs}->{cache} = 1;
447 $rel_rs->set_cache( \@objs );
448
449 while( my $rel_obj = $rel_rs->next ) {
450 $self->build_rr( $rs, $rel_obj );
451 }
452
453 }
454
455}
89c0a5a2 456
87c4e602 457=head2 result_source
701da8c4 458
459Returns a reference to the result source for this recordset.
460
461=cut
462
463
bfab575a 464=head2 count
ee38fa40 465
bfab575a 466Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 467with to find the number of elements. If passed arguments, does a search
468on the resultset and counts the results of that.
ee38fa40 469
bda4c2b8 470Note: When using C<count> with C<group_by>, L<DBIX::Class> emulates C<GROUP BY>
471using C<COUNT( DISTINCT( columns ) )>. Some databases (notably SQLite) do
472not support C<DISTINCT> with multiple columns. If you are using such a
473database, you should only use columns from the main table in your C<group_by>
474clause.
475
ee38fa40 476=cut
477
89c0a5a2 478sub count {
6009260a 479 my $self = shift;
480 return $self->search(@_)->count if @_ && defined $_[0];
6aeb9185 481 unless (defined $self->{count}) {
64acc2bc 482 return scalar @{ $self->get_cache }
483 if @{ $self->get_cache };
15c382be 484 my $group_by;
485 my $select = { 'count' => '*' };
486 if( $group_by = delete $self->{attrs}{group_by} ) {
dec2517f 487 my @distinct = (ref $group_by ? @$group_by : ($group_by));
15c382be 488 # todo: try CONCAT for multi-column pk
489 my @pk = $self->result_source->primary_columns;
490 if( scalar(@pk) == 1 ) {
491 my $pk = shift(@pk);
492 my $alias = $self->{attrs}{alias};
493 my $re = qr/^($alias\.)?$pk$/;
d0f1e63f 494 foreach my $column ( @distinct) {
15c382be 495 if( $column =~ $re ) {
496 @distinct = ( $column );
497 last;
498 }
499 }
500 }
501
502 $select = { count => { 'distinct' => \@distinct } };
503 #use Data::Dumper; die Dumper $select;
504 }
505
976f3686 506 my $attrs = { %{ $self->{attrs} },
15c382be 507 select => $select,
54540863 508 as => [ 'count' ] };
ea20d0fd 509 # offset, order by and page are not needed to count. record_filter is cdbi
510 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
3c5b25c5 511
701da8c4 512 ($self->{count}) = (ref $self)->new($self->result_source, $attrs)->cursor->next;
15c382be 513 $self->{attrs}{group_by} = $group_by;
3c5b25c5 514 }
515 return 0 unless $self->{count};
6aeb9185 516 my $count = $self->{count};
517 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
518 $count = $self->{attrs}{rows} if
519 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
520 return $count;
89c0a5a2 521}
522
bfab575a 523=head2 count_literal
6009260a 524
a33df5d4 525Calls L</search_literal> with the passed arguments, then L</count>.
6009260a 526
527=cut
528
529sub count_literal { shift->search_literal(@_)->count; }
530
bfab575a 531=head2 all
ee38fa40 532
bfab575a 533Returns all elements in the resultset. Called implictly if the resultset
534is returned in list context.
ee38fa40 535
536=cut
537
89c0a5a2 538sub all {
539 my ($self) = @_;
64acc2bc 540 return @{ $self->get_cache }
541 if @{ $self->get_cache };
542 if( $self->{attrs}->{cache} ) {
543 my @obj = map { $self->_construct_object(@$_); }
544 $self->cursor->all;
545 $self->set_cache( \@obj );
546 return @{ $self->get_cache };
547 }
c7ce65e6 548 return map { $self->_construct_object(@$_); }
73f58123 549 $self->cursor->all;
89c0a5a2 550}
551
bfab575a 552=head2 reset
ee38fa40 553
bfab575a 554Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 555
556=cut
557
89c0a5a2 558sub reset {
559 my ($self) = @_;
64acc2bc 560 $self->{all_cache_position} = 0;
73f58123 561 $self->cursor->reset;
89c0a5a2 562 return $self;
563}
564
bfab575a 565=head2 first
ee38fa40 566
bfab575a 567Resets the resultset and returns the first element.
ee38fa40 568
569=cut
570
89c0a5a2 571sub first {
572 return $_[0]->reset->next;
573}
574
87c4e602 575=head2 update
576
577=head3 Arguments: (\%values)
c01ab172 578
a33df5d4 579Sets the specified columns in the resultset to the supplied values.
c01ab172 580
581=cut
582
583sub update {
584 my ($self, $values) = @_;
701da8c4 585 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
586 return $self->result_source->storage->update(
587 $self->result_source->from, $values, $self->{cond});
c01ab172 588}
589
87c4e602 590=head2 update_all
591
592=head3 Arguments: (\%values)
c01ab172 593
a33df5d4 594Fetches all objects and updates them one at a time. Note that C<update_all>
595will run cascade triggers while L</update> will not.
c01ab172 596
597=cut
598
599sub update_all {
600 my ($self, $values) = @_;
701da8c4 601 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
c01ab172 602 foreach my $obj ($self->all) {
603 $obj->set_columns($values)->update;
604 }
605 return 1;
606}
607
bfab575a 608=head2 delete
ee38fa40 609
c01ab172 610Deletes the contents of the resultset from its result source.
ee38fa40 611
612=cut
613
28927b50 614sub delete {
89c0a5a2 615 my ($self) = @_;
ca4b5ab7 616 my $del = {};
617 $self->throw_exception("Can't delete on resultset with condition unless hash or array")
618 unless (ref($self->{cond}) eq 'HASH' || ref($self->{cond}) eq 'ARRAY');
619 if (ref $self->{cond} eq 'ARRAY') {
620 $del = [ map { my %hash;
621 foreach my $key (keys %{$_}) {
622 $key =~ /([^\.]+)$/;
623 $hash{$1} = $_->{$key};
624 }; \%hash; } @{$self->{cond}} ];
625 } elsif ((keys %{$self->{cond}})[0] eq '-and') {
626 $del->{-and} = [ map { my %hash;
627 foreach my $key (keys %{$_}) {
628 $key =~ /([^\.]+)$/;
629 $hash{$1} = $_->{$key};
630 }; \%hash; } @{$self->{cond}{-and}} ];
631 } else {
632 foreach my $key (keys %{$self->{cond}}) {
633 $key =~ /([^\.]+)$/;
634 $del->{$1} = $self->{cond}{$key};
635 }
636 }
637 $self->result_source->storage->delete($self->result_source->from, $del);
89c0a5a2 638 return 1;
639}
640
c01ab172 641=head2 delete_all
642
a33df5d4 643Fetches all objects and deletes them one at a time. Note that C<delete_all>
644will run cascade triggers while L</delete> will not.
c01ab172 645
646=cut
647
648sub delete_all {
649 my ($self) = @_;
650 $_->delete for $self->all;
651 return 1;
652}
28927b50 653
bfab575a 654=head2 pager
ee38fa40 655
656Returns a L<Data::Page> object for the current resultset. Only makes
a33df5d4 657sense for queries with a C<page> attribute.
ee38fa40 658
659=cut
660
3c5b25c5 661sub pager {
662 my ($self) = @_;
663 my $attrs = $self->{attrs};
701da8c4 664 $self->throw_exception("Can't create pager for non-paged rs") unless $self->{page};
6aeb9185 665 $attrs->{rows} ||= 10;
666 $self->count;
667 return $self->{pager} ||= Data::Page->new(
93b004d3 668 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 669}
670
87c4e602 671=head2 page
672
673=head3 Arguments: ($page_num)
ee38fa40 674
bfab575a 675Returns a new resultset for the specified page.
ee38fa40 676
677=cut
678
3c5b25c5 679sub page {
680 my ($self, $page) = @_;
6aeb9185 681 my $attrs = { %{$self->{attrs}} };
3c5b25c5 682 $attrs->{page} = $page;
701da8c4 683 return (ref $self)->new($self->result_source, $attrs);
fea3d045 684}
685
87c4e602 686=head2 new_result
687
688=head3 Arguments: (\%vals)
fea3d045 689
87f0da6a 690Creates a result in the resultset's result class.
fea3d045 691
692=cut
693
694sub new_result {
695 my ($self, $values) = @_;
701da8c4 696 $self->throw_exception( "new_result needs a hash" )
fea3d045 697 unless (ref $values eq 'HASH');
701da8c4 698 $self->throw_exception( "Can't abstract implicit construct, condition not a hash" )
fea3d045 699 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
700 my %new = %$values;
701 my $alias = $self->{attrs}{alias};
702 foreach my $key (keys %{$self->{cond}||{}}) {
703 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
704 }
701da8c4 705 my $obj = $self->result_source->result_class->new(\%new);
706 $obj->result_source($self->result_source) if $obj->can('result_source');
097d3227 707 $obj;
fea3d045 708}
709
87c4e602 710=head2 create
711
712=head3 Arguments: (\%vals)
fea3d045 713
87f0da6a 714Inserts a record into the resultset and returns the object.
fea3d045 715
a33df5d4 716Effectively a shortcut for C<< ->new_result(\%vals)->insert >>.
fea3d045 717
718=cut
719
720sub create {
721 my ($self, $attrs) = @_;
701da8c4 722 $self->throw_exception( "create needs a hashref" ) unless ref $attrs eq 'HASH';
fea3d045 723 return $self->new_result($attrs)->insert;
3c5b25c5 724}
725
87c4e602 726=head2 find_or_create
727
728=head3 Arguments: (\%vals, \%attrs?)
87f0da6a 729
730 $class->find_or_create({ key => $val, ... });
c2b15ecc 731
c2b15ecc 732Searches for a record matching the search condition; if it doesn't find one,
cf7b40ed 733creates one and returns that instead.
87f0da6a 734
87f0da6a 735 my $cd = $schema->resultset('CD')->find_or_create({
736 cdid => 5,
737 artist => 'Massive Attack',
738 title => 'Mezzanine',
739 year => 2005,
740 });
741
742Also takes an optional C<key> attribute, to search by a specific key or unique
743constraint. For example:
744
745 my $cd = $schema->resultset('CD')->find_or_create(
746 {
747 artist => 'Massive Attack',
748 title => 'Mezzanine',
749 },
750 { key => 'artist_title' }
751 );
752
753See also L</find> and L</update_or_create>.
754
c2b15ecc 755=cut
756
757sub find_or_create {
758 my $self = shift;
87f0da6a 759 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
760 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
761 my $exists = $self->find($hash, $attrs);
c2b15ecc 762 return defined($exists) ? $exists : $self->create($hash);
763}
764
87f0da6a 765=head2 update_or_create
766
767 $class->update_or_create({ key => $val, ... });
768
769First, search for an existing row matching one of the unique constraints
770(including the primary key) on the source of this resultset. If a row is
771found, update it with the other given column values. Otherwise, create a new
772row.
773
774Takes an optional C<key> attribute to search on a specific unique constraint.
775For example:
776
777 # In your application
778 my $cd = $schema->resultset('CD')->update_or_create(
779 {
780 artist => 'Massive Attack',
781 title => 'Mezzanine',
782 year => 1998,
783 },
784 { key => 'artist_title' }
785 );
786
787If no C<key> is specified, it searches on all unique constraints defined on the
788source, including the primary key.
789
790If the C<key> is specified as C<primary>, search only on the primary key.
791
a33df5d4 792See also L</find> and L</find_or_create>.
793
87f0da6a 794=cut
795
796sub update_or_create {
797 my $self = shift;
798
799 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
800 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
801
701da8c4 802 my %unique_constraints = $self->result_source->unique_constraints;
87f0da6a 803 my @constraint_names = (exists $attrs->{key}
804 ? ($attrs->{key})
805 : keys %unique_constraints);
806
807 my @unique_hashes;
808 foreach my $name (@constraint_names) {
809 my @unique_cols = @{ $unique_constraints{$name} };
810 my %unique_hash =
811 map { $_ => $hash->{$_} }
812 grep { exists $hash->{$_} }
813 @unique_cols;
814
815 push @unique_hashes, \%unique_hash
816 if (scalar keys %unique_hash == scalar @unique_cols);
817 }
818
819 my $row;
820 if (@unique_hashes) {
821 $row = $self->search(\@unique_hashes, { rows => 1 })->first;
822 if ($row) {
823 $row->set_columns($hash);
824 $row->update;
825 }
826 }
827
828 unless ($row) {
829 $row = $self->create($hash);
830 }
831
832 return $row;
833}
834
64acc2bc 835=head2 get_cache
836
837Gets the contents of the cache for the resultset.
838
839=cut
840
841sub get_cache {
842 my $self = shift;
843 return $self->{all_cache} || [];
844}
845
846=head2 set_cache
847
848Sets the contents of the cache for the resultset. Expects an arrayref of objects of the same class as those produced by the resultset.
849
850=cut
851
852sub set_cache {
853 my ( $self, $data ) = @_;
854 $self->throw_exception("set_cache requires an arrayref")
855 if ref $data ne 'ARRAY';
856 my $result_class = $self->result_source->result_class;
857 foreach( @$data ) {
858 $self->throw_exception("cannot cache object of type '$_', expected '$result_class'")
859 if ref $_ ne $result_class;
860 }
861 $self->{all_cache} = $data;
862}
863
864=head2 clear_cache
865
866Clears the cache for the resultset.
867
868=cut
869
870sub clear_cache {
871 my $self = shift;
872 $self->set_cache([]);
873}
874
875=head2 related_resultset
876
877Returns a related resultset for the supplied relationship name.
878
879 $rs = $rs->related_resultset('foo');
880
881=cut
882
883sub related_resultset {
884 my ( $self, $rel, @rest ) = @_;
885 $self->{related_resultsets} ||= {};
886 my $resultsets = $self->{related_resultsets};
887 if( !exists $resultsets->{$rel} ) {
888 #warn "fetching related resultset for rel '$rel'";
889 my $rel_obj = $self->result_source->relationship_info($rel);
890 $self->throw_exception(
891 "search_related: result source '" . $self->result_source->name .
892 "' has no such relationship ${rel}")
893 unless $rel_obj; #die Dumper $self->{attrs};
894 my $rs;
895 if( $self->{attrs}->{cache} ) {
896 $rs = $self->search(undef);
897 }
898 else {
899 $rs = $self->search(undef, { join => $rel });
900 }
901 #use Data::Dumper; die Dumper $rs->{attrs};#$rs = $self->search( undef );
902 #use Data::Dumper; warn Dumper $self->{attrs}, Dumper $rs->{attrs};
903 my $alias = (defined $rs->{attrs}{seen_join}{$rel}
904 && $rs->{attrs}{seen_join}{$rel} > 1
905 ? join('_', $rel, $rs->{attrs}{seen_join}{$rel})
906 : $rel);
907 $resultsets->{$rel} =
908 $self->result_source->schema->resultset($rel_obj->{class}
909 )->search( undef,
910 { %{$rs->{attrs}},
911 alias => $alias,
912 select => undef(),
913 as => undef() }
914 )->search(@rest);
915 }
916 return $resultsets->{$rel};
917}
918
701da8c4 919=head2 throw_exception
920
921See Schema's throw_exception
922
923=cut
924
925sub throw_exception {
926 my $self=shift;
927 $self->result_source->schema->throw_exception(@_);
928}
929
40dbc108 930=head1 ATTRIBUTES
076652e8 931
a33df5d4 932The resultset takes various attributes that modify its behavior. Here's an
933overview of them:
bfab575a 934
935=head2 order_by
076652e8 936
a33df5d4 937Which column(s) to order the results by. This is currently passed through
938directly to SQL, so you can give e.g. C<foo DESC> for a descending order.
076652e8 939
87c4e602 940=head2 cols
941
942=head3 Arguments: (arrayref)
976f3686 943
a33df5d4 944Shortcut to request a particular set of columns to be retrieved. Adds
945C<me.> onto the start of any column without a C<.> in it and sets C<select>
946from that, then auto-populates C<as> from C<select> as normal.
976f3686 947
87c4e602 948=head2 include_columns
949
950=head3 Arguments: (arrayref)
5ac6a044 951
952Shortcut to include additional columns in the returned results - for example
953
954 { include_columns => ['foo.name'], join => ['foo'] }
955
956would add a 'name' column to the information passed to object inflation
957
87c4e602 958=head2 select
959
960=head3 Arguments: (arrayref)
976f3686 961
4a28c340 962Indicates which columns should be selected from the storage. You can use
963column names, or in the case of RDBMS back ends, function or stored procedure
964names:
965
966 $rs = $schema->resultset('Foo')->search(
967 {},
968 {
cf7b40ed 969 select => [
4a28c340 970 'column_name',
971 { count => 'column_to_count' },
972 { sum => 'column_to_sum' }
cf7b40ed 973 ]
4a28c340 974 }
975 );
976
977When you use function/stored procedure names and do not supply an C<as>
978attribute, the column names returned are storage-dependent. E.g. MySQL would
979return a column named C<count(column_to_count)> in the above example.
976f3686 980
87c4e602 981=head2 as
982
983=head3 Arguments: (arrayref)
076652e8 984
4a28c340 985Indicates column names for object inflation. This is used in conjunction with
986C<select>, usually when C<select> contains one or more function or stored
987procedure names:
988
989 $rs = $schema->resultset('Foo')->search(
990 {},
991 {
cf7b40ed 992 select => [
4a28c340 993 'column1',
994 { count => 'column2' }
cf7b40ed 995 ],
4a28c340 996 as => [qw/ column1 column2_count /]
997 }
998 );
999
1000 my $foo = $rs->first(); # get the first Foo
1001
1002If the object against which the search is performed already has an accessor
1003matching a column name specified in C<as>, the value can be retrieved using
1004the accessor as normal:
1005
1006 my $column1 = $foo->column1();
1007
1008If on the other hand an accessor does not exist in the object, you need to
1009use C<get_column> instead:
1010
1011 my $column2_count = $foo->get_column('column2_count');
1012
1013You can create your own accessors if required - see
1014L<DBIx::Class::Manual::Cookbook> for details.
ee38fa40 1015
bfab575a 1016=head2 join
ee38fa40 1017
a33df5d4 1018Contains a list of relationships that should be joined for this query. For
1019example:
1020
1021 # Get CDs by Nine Inch Nails
1022 my $rs = $schema->resultset('CD')->search(
1023 { 'artist.name' => 'Nine Inch Nails' },
1024 { join => 'artist' }
1025 );
1026
1027Can also contain a hash reference to refer to the other relation's relations.
1028For example:
1029
1030 package MyApp::Schema::Track;
1031 use base qw/DBIx::Class/;
1032 __PACKAGE__->table('track');
1033 __PACKAGE__->add_columns(qw/trackid cd position title/);
1034 __PACKAGE__->set_primary_key('trackid');
1035 __PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
1036 1;
1037
1038 # In your application
1039 my $rs = $schema->resultset('Artist')->search(
1040 { 'track.title' => 'Teardrop' },
1041 {
1042 join => { cd => 'track' },
1043 order_by => 'artist.name',
1044 }
1045 );
1046
2cb360cc 1047If the same join is supplied twice, it will be aliased to <rel>_2 (and
1048similarly for a third time). For e.g.
1049
1050 my $rs = $schema->resultset('Artist')->search(
1051 { 'cds.title' => 'Foo',
1052 'cds_2.title' => 'Bar' },
1053 { join => [ qw/cds cds/ ] });
1054
1055will return a set of all artists that have both a cd with title Foo and a cd
1056with title Bar.
1057
1058If you want to fetch related objects from other tables as well, see C<prefetch>
ae1c90a1 1059below.
ee38fa40 1060
87c4e602 1061=head2 prefetch
1062
1063=head3 Arguments: arrayref/hashref
ee38fa40 1064
ae1c90a1 1065Contains one or more relationships that should be fetched along with the main
bfab575a 1066query (when they are accessed afterwards they will have already been
a33df5d4 1067"prefetched"). This is useful for when you know you will need the related
ae1c90a1 1068objects, because it saves at least one query:
1069
1070 my $rs = $schema->resultset('Tag')->search(
1071 {},
1072 {
1073 prefetch => {
1074 cd => 'artist'
1075 }
1076 }
1077 );
1078
1079The initial search results in SQL like the following:
1080
1081 SELECT tag.*, cd.*, artist.* FROM tag
1082 JOIN cd ON tag.cd = cd.cdid
1083 JOIN artist ON cd.artist = artist.artistid
1084
1085L<DBIx::Class> has no need to go back to the database when we access the
1086C<cd> or C<artist> relationships, which saves us two SQL statements in this
1087case.
1088
2cb360cc 1089Simple prefetches will be joined automatically, so there is no need
1090for a C<join> attribute in the above search. If you're prefetching to
1091depth (e.g. { cd => { artist => 'label' } or similar), you'll need to
1092specify the join as well.
ae1c90a1 1093
1094C<prefetch> can be used with the following relationship types: C<belongs_to>,
2cb360cc 1095C<has_one> (or if you're using C<add_relationship>, any relationship declared
1096with an accessor type of 'single' or 'filter').
ee38fa40 1097
87c4e602 1098=head2 from
1099
1100=head3 Arguments: (arrayref)
ee38fa40 1101
4a28c340 1102The C<from> attribute gives you manual control over the C<FROM> clause of SQL
1103statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
1104clauses.
ee38fa40 1105
a33df5d4 1106NOTE: Use this on your own risk. This allows you to shoot off your foot!
4a28c340 1107C<join> will usually do what you need and it is strongly recommended that you
1108avoid using C<from> unless you cannot achieve the desired result using C<join>.
1109
1110In simple terms, C<from> works as follows:
1111
1112 [
1113 { <alias> => <table>, -join-type => 'inner|left|right' }
1114 [] # nested JOIN (optional)
1115 { <table.column> = <foreign_table.foreign_key> }
1116 ]
1117
1118 JOIN
1119 <alias> <table>
1120 [JOIN ...]
1121 ON <table.column> = <foreign_table.foreign_key>
1122
1123An easy way to follow the examples below is to remember the following:
1124
1125 Anything inside "[]" is a JOIN
1126 Anything inside "{}" is a condition for the enclosing JOIN
1127
1128The following examples utilize a "person" table in a family tree application.
1129In order to express parent->child relationships, this table is self-joined:
1130
1131 # Person->belongs_to('father' => 'Person');
1132 # Person->belongs_to('mother' => 'Person');
1133
1134C<from> can be used to nest joins. Here we return all children with a father,
1135then search against all mothers of those children:
1136
1137 $rs = $schema->resultset('Person')->search(
1138 {},
1139 {
1140 alias => 'mother', # alias columns in accordance with "from"
1141 from => [
1142 { mother => 'person' },
1143 [
1144 [
1145 { child => 'person' },
1146 [
1147 { father => 'person' },
1148 { 'father.person_id' => 'child.father_id' }
1149 ]
1150 ],
1151 { 'mother.person_id' => 'child.mother_id' }
1152 ],
1153 ]
1154 },
1155 );
1156
1157 # Equivalent SQL:
1158 # SELECT mother.* FROM person mother
1159 # JOIN (
1160 # person child
1161 # JOIN person father
1162 # ON ( father.person_id = child.father_id )
1163 # )
1164 # ON ( mother.person_id = child.mother_id )
1165
1166The type of any join can be controlled manually. To search against only people
1167with a father in the person table, we could explicitly use C<INNER JOIN>:
1168
1169 $rs = $schema->resultset('Person')->search(
1170 {},
1171 {
1172 alias => 'child', # alias columns in accordance with "from"
1173 from => [
1174 { child => 'person' },
1175 [
1176 { father => 'person', -join-type => 'inner' },
1177 { 'father.id' => 'child.father_id' }
1178 ],
1179 ]
1180 },
1181 );
1182
1183 # Equivalent SQL:
1184 # SELECT child.* FROM person child
1185 # INNER JOIN person father ON child.father_id = father.id
ee38fa40 1186
bfab575a 1187=head2 page
076652e8 1188
a33df5d4 1189For a paged resultset, specifies which page to retrieve. Leave unset
bfab575a 1190for an unpaged resultset.
076652e8 1191
bfab575a 1192=head2 rows
076652e8 1193
4a28c340 1194For a paged resultset, how many rows per page:
1195
1196 rows => 10
1197
1198Can also be used to simulate an SQL C<LIMIT>.
076652e8 1199
87c4e602 1200=head2 group_by
1201
1202=head3 Arguments: (arrayref)
54540863 1203
bda4c2b8 1204A arrayref of columns to group by. Can include columns of joined tables.
54540863 1205
675ce4a6 1206 group_by => [qw/ column1 column2 ... /]
1207
54540863 1208=head2 distinct
1209
a33df5d4 1210Set to 1 to group by all columns.
1211
1212For more examples of using these attributes, see
1213L<DBIx::Class::Manual::Cookbook>.
54540863 1214
bfab575a 1215=cut
076652e8 1216
89c0a5a2 12171;