handle the throw_exception bit. Drop DBIx::Class::Exception
[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
976f3686 51=head2 new($source, \%$attrs)
ee38fa40 52
a33df5d4 53The resultset constructor. Takes a source object (usually a
80c90f5d 54L<DBIx::Class::ResultSourceProxy::Table>) and an attribute hash (see L</ATRRIBUTES>
a33df5d4 55below). Does not perform any queries -- these are executed as needed by the
56other methods.
57
58Generally you won't need to construct a resultset manually. You'll
59automatically get one from e.g. a L</search> called in scalar context:
60
61 my $rs = $schema->resultset('CD')->search({ title => '100th Window' });
ee38fa40 62
63=cut
64
89c0a5a2 65sub new {
fea3d045 66 my $class = shift;
f9db5527 67 return $class->new_result(@_) if ref $class;
fea3d045 68 my ($source, $attrs) = @_;
b98e75f6 69 #use Data::Dumper; warn Dumper($attrs);
ea20d0fd 70 $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
c7ce65e6 71 my %seen;
6aeb9185 72 my $alias = ($attrs->{alias} ||= 'me');
a9433341 73 if ($attrs->{cols} || !$attrs->{select}) {
74 delete $attrs->{as} if $attrs->{cols};
976f3686 75 my @cols = ($attrs->{cols}
76 ? @{delete $attrs->{cols}}
a9433341 77 : $source->columns);
6aeb9185 78 $attrs->{select} = [ map { m/\./ ? $_ : "${alias}.$_" } @cols ];
976f3686 79 }
6aeb9185 80 $attrs->{as} ||= [ map { m/^$alias\.(.*)$/ ? $1 : $_ } @{$attrs->{select}} ];
976f3686 81 #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
fea3d045 82 $attrs->{from} ||= [ { $alias => $source->from } ];
b52e9bf8 83 if (my $join = delete $attrs->{join}) {
84 foreach my $j (ref $join eq 'ARRAY'
85 ? (@{$join}) : ($join)) {
c7ce65e6 86 if (ref $j eq 'HASH') {
87 $seen{$_} = 1 foreach keys %$j;
88 } else {
89 $seen{$j} = 1;
90 }
91 }
8452e496 92 push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
c7ce65e6 93 }
54540863 94 $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
b3e8ac9b 95
96 if (my $prefetch = delete $attrs->{prefetch}) {
97 foreach my $p (ref $prefetch eq 'ARRAY'
98 ? (@{$prefetch}) : ($prefetch)) {
99 if( ref $p eq 'HASH' ) {
100 foreach my $key (keys %$p) {
101 push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
102 unless $seen{$key};
103 }
104 }
105 else {
106 push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
107 unless $seen{$p};
108 }
109 my @cols = ();
110 push @cols, $source->resolve_prefetch($p, $attrs->{alias});
111 #die Dumper \@cols;
112 push(@{$attrs->{select}}, @cols);
113 push(@{$attrs->{as}}, @cols);
114 }
fef5d100 115 }
b3e8ac9b 116
6aeb9185 117 if ($attrs->{page}) {
118 $attrs->{rows} ||= 10;
119 $attrs->{offset} ||= 0;
120 $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
121 }
89c0a5a2 122 my $new = {
701da8c4 123 result_source => $source,
89c0a5a2 124 cond => $attrs->{where},
0a3c5b43 125 from => $attrs->{from},
3c5b25c5 126 count => undef,
93b004d3 127 page => delete $attrs->{page},
3c5b25c5 128 pager => undef,
89c0a5a2 129 attrs => $attrs };
2f5911b2 130 bless ($new, $class);
9229f20a 131 return $new;
89c0a5a2 132}
133
bfab575a 134=head2 search
0a3c5b43 135
87f0da6a 136 my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3"
137 my $new_rs = $rs->search({ foo => 3 });
138
6009260a 139If you need to pass in additional attributes but no additional condition,
a33df5d4 140call it as C<search({}, \%attrs);>.
87f0da6a 141
a33df5d4 142 # "SELECT foo, bar FROM $class_table"
143 my @all = $class->search({}, { cols => [qw/foo bar/] });
0a3c5b43 144
145=cut
146
147sub search {
148 my $self = shift;
149
6009260a 150 #use Data::Dumper;warn Dumper(@_);
151
0a3c5b43 152 my $attrs = { %{$self->{attrs}} };
153 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
6aeb9185 154 $attrs = { %$attrs, %{ pop(@_) } };
0a3c5b43 155 }
156
6aeb9185 157 my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
0a3c5b43 158 if (defined $where) {
159 $where = (defined $attrs->{where}
ad3d2d7c 160 ? { '-and' =>
161 [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
162 $where, $attrs->{where} ] }
0a3c5b43 163 : $where);
164 $attrs->{where} = $where;
165 }
166
701da8c4 167 my $rs = (ref $self)->new($self->result_source, $attrs);
0a3c5b43 168
169 return (wantarray ? $rs->all : $rs);
170}
171
87f0da6a 172=head2 search_literal
173
6009260a 174 my @obj = $rs->search_literal($literal_where_cond, @bind);
175 my $new_rs = $rs->search_literal($literal_where_cond, @bind);
176
177Pass a literal chunk of SQL to be added to the conditional part of the
87f0da6a 178resultset.
6009260a 179
bfab575a 180=cut
181
6009260a 182sub search_literal {
183 my ($self, $cond, @vals) = @_;
184 my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
185 $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
186 return $self->search(\$cond, $attrs);
187}
0a3c5b43 188
87f0da6a 189=head2 find(@colvalues), find(\%cols, \%attrs?)
190
191Finds a row based on its primary key or unique constraint. For example:
192
87f0da6a 193 my $cd = $schema->resultset('CD')->find(5);
194
195Also takes an optional C<key> attribute, to search by a specific key or unique
196constraint. For example:
197
198 my $cd = $schema->resultset('CD')->find_or_create(
199 {
200 artist => 'Massive Attack',
201 title => 'Mezzanine',
202 },
203 { key => 'artist_title' }
204 );
205
a33df5d4 206See also L</find_or_create> and L</update_or_create>.
207
87f0da6a 208=cut
716b3d29 209
210sub find {
211 my ($self, @vals) = @_;
212 my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
87f0da6a 213
701da8c4 214 my @cols = $self->result_source->primary_columns;
87f0da6a 215 if (exists $attrs->{key}) {
701da8c4 216 my %uniq = $self->result_source->unique_constraints;
87f0da6a 217 $self->( "Unknown key " . $attrs->{key} . " on " . $self->name )
218 unless exists $uniq{$attrs->{key}};
219 @cols = @{ $uniq{$attrs->{key}} };
220 }
221 #use Data::Dumper; warn Dumper($attrs, @vals, @cols);
701da8c4 222 $self->throw_exception( "Can't find unless a primary key or unique constraint is defined" )
87f0da6a 223 unless @cols;
224
716b3d29 225 my $query;
226 if (ref $vals[0] eq 'HASH') {
227 $query = $vals[0];
87f0da6a 228 } elsif (@cols == @vals) {
716b3d29 229 $query = {};
87f0da6a 230 @{$query}{@cols} = @vals;
716b3d29 231 } else {
232 $query = {@vals};
233 }
234 #warn Dumper($query);
716b3d29 235 return $self->search($query)->next;
236}
237
b52e9bf8 238=head2 search_related
239
240 $rs->search_related('relname', $cond?, $attrs?);
241
a33df5d4 242Search the specified relationship. Optionally specify a condition for matching
243records.
244
b52e9bf8 245=cut
246
6aeb9185 247sub search_related {
248 my ($self, $rel, @rest) = @_;
701da8c4 249 my $rel_obj = $self->result_source->relationship_info($rel);
250 $self->throw_exception(
6aeb9185 251 "No such relationship ${rel} in search_related")
252 unless $rel_obj;
6aeb9185 253 my $rs = $self->search(undef, { join => $rel });
701da8c4 254 return $self->result_source->schema->resultset($rel_obj->{class}
ea20d0fd 255 )->search( undef,
256 { %{$rs->{attrs}},
257 alias => $rel,
258 select => undef(),
259 as => undef() }
6aeb9185 260 )->search(@rest);
261}
b52e9bf8 262
bfab575a 263=head2 cursor
ee38fa40 264
bfab575a 265Returns a storage-driven cursor to the given resultset.
ee38fa40 266
267=cut
268
73f58123 269sub cursor {
270 my ($self) = @_;
701da8c4 271 my ($attrs) = $self->{attrs};
6aeb9185 272 $attrs = { %$attrs };
73f58123 273 return $self->{cursor}
701da8c4 274 ||= $self->result_source->storage->select($self->{from}, $attrs->{select},
73f58123 275 $attrs->{where},$attrs);
276}
277
87f0da6a 278=head2 search_like
279
a33df5d4 280Perform a search, but use C<LIKE> instead of equality as the condition. Note
281that this is simply a convenience method; you most likely want to use
282L</search> with specific operators.
283
284For more information, see L<DBIx::Class::Manual::Cookbook>.
87f0da6a 285
286=cut
58a4bd18 287
288sub search_like {
289 my $class = shift;
290 my $attrs = { };
291 if (@_ > 1 && ref $_[$#_] eq 'HASH') {
292 $attrs = pop(@_);
293 }
294 my $query = ref $_[0] eq "HASH" ? { %{shift()} }: {@_};
295 $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
296 return $class->search($query, { %$attrs });
297}
298
bfab575a 299=head2 slice($first, $last)
ee38fa40 300
bfab575a 301Returns a subset of elements from the resultset.
ee38fa40 302
303=cut
304
89c0a5a2 305sub slice {
306 my ($self, $min, $max) = @_;
307 my $attrs = { %{ $self->{attrs} || {} } };
6aeb9185 308 $attrs->{offset} ||= 0;
309 $attrs->{offset} += $min;
89c0a5a2 310 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
701da8c4 311 my $slice = (ref $self)->new($self->result_source, $attrs);
89c0a5a2 312 return (wantarray ? $slice->all : $slice);
313}
314
87f0da6a 315=head2 next
ee38fa40 316
a33df5d4 317Returns the next element in the resultset (C<undef> is there is none).
318
319Can be used to efficiently iterate over records in the resultset:
320
321 my $rs = $schema->resultset('CD')->search({});
322 while (my $cd = $rs->next) {
323 print $cd->title;
324 }
ee38fa40 325
326=cut
327
89c0a5a2 328sub next {
329 my ($self) = @_;
73f58123 330 my @row = $self->cursor->next;
a953d8d9 331# warn Dumper(\@row); use Data::Dumper;
89c0a5a2 332 return unless (@row);
c7ce65e6 333 return $self->_construct_object(@row);
334}
335
336sub _construct_object {
337 my ($self, @row) = @_;
b3e8ac9b 338 my @as = @{ $self->{attrs}{as} };
976f3686 339 #warn "@cols -> @row";
b3e8ac9b 340 my $info = [ {}, {} ];
341 foreach my $as (@as) {
342 my $target = $info;
343 my @parts = split(/\./, $as);
344 my $col = pop(@parts);
345 foreach my $p (@parts) {
346 $target = $target->[1]->{$p} ||= [];
c7ce65e6 347 }
b3e8ac9b 348 $target->[0]->{$col} = shift @row;
c7ce65e6 349 }
b3e8ac9b 350 #use Data::Dumper; warn Dumper(\@as, $info);
701da8c4 351 my $new = $self->result_source->result_class->inflate_result(
352 $self->result_source, @$info);
33ce49d6 353 $new = $self->{attrs}{record_filter}->($new)
354 if exists $self->{attrs}{record_filter};
355 return $new;
89c0a5a2 356}
357
701da8c4 358=head2 result_source
359
360Returns a reference to the result source for this recordset.
361
362=cut
363
364
bfab575a 365=head2 count
ee38fa40 366
bfab575a 367Performs an SQL C<COUNT> with the same query as the resultset was built
6009260a 368with to find the number of elements. If passed arguments, does a search
369on the resultset and counts the results of that.
ee38fa40 370
371=cut
372
89c0a5a2 373sub count {
6009260a 374 my $self = shift;
375 return $self->search(@_)->count if @_ && defined $_[0];
701da8c4 376 $self->throw_exception(
377 "Unable to ->count with a GROUP BY"
378 ) if defined $self->{attrs}{group_by};
6aeb9185 379 unless (defined $self->{count}) {
976f3686 380 my $attrs = { %{ $self->{attrs} },
54540863 381 select => { 'count' => '*' },
382 as => [ 'count' ] };
ea20d0fd 383 # offset, order by and page are not needed to count. record_filter is cdbi
384 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
3c5b25c5 385
701da8c4 386 ($self->{count}) = (ref $self)->new($self->result_source, $attrs)->cursor->next;
3c5b25c5 387 }
388 return 0 unless $self->{count};
6aeb9185 389 my $count = $self->{count};
390 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
391 $count = $self->{attrs}{rows} if
392 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
393 return $count;
89c0a5a2 394}
395
bfab575a 396=head2 count_literal
6009260a 397
a33df5d4 398Calls L</search_literal> with the passed arguments, then L</count>.
6009260a 399
400=cut
401
402sub count_literal { shift->search_literal(@_)->count; }
403
bfab575a 404=head2 all
ee38fa40 405
bfab575a 406Returns all elements in the resultset. Called implictly if the resultset
407is returned in list context.
ee38fa40 408
409=cut
410
89c0a5a2 411sub all {
412 my ($self) = @_;
c7ce65e6 413 return map { $self->_construct_object(@$_); }
73f58123 414 $self->cursor->all;
89c0a5a2 415}
416
bfab575a 417=head2 reset
ee38fa40 418
bfab575a 419Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 420
421=cut
422
89c0a5a2 423sub reset {
424 my ($self) = @_;
73f58123 425 $self->cursor->reset;
89c0a5a2 426 return $self;
427}
428
bfab575a 429=head2 first
ee38fa40 430
bfab575a 431Resets the resultset and returns the first element.
ee38fa40 432
433=cut
434
89c0a5a2 435sub first {
436 return $_[0]->reset->next;
437}
438
c01ab172 439=head2 update(\%values)
440
a33df5d4 441Sets the specified columns in the resultset to the supplied values.
c01ab172 442
443=cut
444
445sub update {
446 my ($self, $values) = @_;
701da8c4 447 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
448 return $self->result_source->storage->update(
449 $self->result_source->from, $values, $self->{cond});
c01ab172 450}
451
452=head2 update_all(\%values)
453
a33df5d4 454Fetches all objects and updates them one at a time. Note that C<update_all>
455will run cascade triggers while L</update> will not.
c01ab172 456
457=cut
458
459sub update_all {
460 my ($self, $values) = @_;
701da8c4 461 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
c01ab172 462 foreach my $obj ($self->all) {
463 $obj->set_columns($values)->update;
464 }
465 return 1;
466}
467
bfab575a 468=head2 delete
ee38fa40 469
c01ab172 470Deletes the contents of the resultset from its result source.
ee38fa40 471
472=cut
473
28927b50 474sub delete {
89c0a5a2 475 my ($self) = @_;
701da8c4 476 $self->result_source->storage->delete($self->result_source->from, $self->{cond});
89c0a5a2 477 return 1;
478}
479
c01ab172 480=head2 delete_all
481
a33df5d4 482Fetches all objects and deletes them one at a time. Note that C<delete_all>
483will run cascade triggers while L</delete> will not.
c01ab172 484
485=cut
486
487sub delete_all {
488 my ($self) = @_;
489 $_->delete for $self->all;
490 return 1;
491}
28927b50 492
bfab575a 493=head2 pager
ee38fa40 494
495Returns a L<Data::Page> object for the current resultset. Only makes
a33df5d4 496sense for queries with a C<page> attribute.
ee38fa40 497
498=cut
499
3c5b25c5 500sub pager {
501 my ($self) = @_;
502 my $attrs = $self->{attrs};
701da8c4 503 $self->throw_exception("Can't create pager for non-paged rs") unless $self->{page};
6aeb9185 504 $attrs->{rows} ||= 10;
505 $self->count;
506 return $self->{pager} ||= Data::Page->new(
93b004d3 507 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 508}
509
bfab575a 510=head2 page($page_num)
ee38fa40 511
bfab575a 512Returns a new resultset for the specified page.
ee38fa40 513
514=cut
515
3c5b25c5 516sub page {
517 my ($self, $page) = @_;
6aeb9185 518 my $attrs = { %{$self->{attrs}} };
3c5b25c5 519 $attrs->{page} = $page;
701da8c4 520 return (ref $self)->new($self->result_source, $attrs);
fea3d045 521}
522
523=head2 new_result(\%vals)
524
87f0da6a 525Creates a result in the resultset's result class.
fea3d045 526
527=cut
528
529sub new_result {
530 my ($self, $values) = @_;
701da8c4 531 $self->throw_exception( "new_result needs a hash" )
fea3d045 532 unless (ref $values eq 'HASH');
701da8c4 533 $self->throw_exception( "Can't abstract implicit construct, condition not a hash" )
fea3d045 534 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
535 my %new = %$values;
536 my $alias = $self->{attrs}{alias};
537 foreach my $key (keys %{$self->{cond}||{}}) {
538 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
539 }
701da8c4 540 my $obj = $self->result_source->result_class->new(\%new);
541 $obj->result_source($self->result_source) if $obj->can('result_source');
097d3227 542 $obj;
fea3d045 543}
544
545=head2 create(\%vals)
546
87f0da6a 547Inserts a record into the resultset and returns the object.
fea3d045 548
a33df5d4 549Effectively a shortcut for C<< ->new_result(\%vals)->insert >>.
fea3d045 550
551=cut
552
553sub create {
554 my ($self, $attrs) = @_;
701da8c4 555 $self->throw_exception( "create needs a hashref" ) unless ref $attrs eq 'HASH';
fea3d045 556 return $self->new_result($attrs)->insert;
3c5b25c5 557}
558
87f0da6a 559=head2 find_or_create(\%vals, \%attrs?)
560
561 $class->find_or_create({ key => $val, ... });
c2b15ecc 562
c2b15ecc 563Searches for a record matching the search condition; if it doesn't find one,
cf7b40ed 564creates one and returns that instead.
87f0da6a 565
87f0da6a 566 my $cd = $schema->resultset('CD')->find_or_create({
567 cdid => 5,
568 artist => 'Massive Attack',
569 title => 'Mezzanine',
570 year => 2005,
571 });
572
573Also takes an optional C<key> attribute, to search by a specific key or unique
574constraint. For example:
575
576 my $cd = $schema->resultset('CD')->find_or_create(
577 {
578 artist => 'Massive Attack',
579 title => 'Mezzanine',
580 },
581 { key => 'artist_title' }
582 );
583
584See also L</find> and L</update_or_create>.
585
c2b15ecc 586=cut
587
588sub find_or_create {
589 my $self = shift;
87f0da6a 590 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
591 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
592 my $exists = $self->find($hash, $attrs);
c2b15ecc 593 return defined($exists) ? $exists : $self->create($hash);
594}
595
87f0da6a 596=head2 update_or_create
597
598 $class->update_or_create({ key => $val, ... });
599
600First, search for an existing row matching one of the unique constraints
601(including the primary key) on the source of this resultset. If a row is
602found, update it with the other given column values. Otherwise, create a new
603row.
604
605Takes an optional C<key> attribute to search on a specific unique constraint.
606For example:
607
608 # In your application
609 my $cd = $schema->resultset('CD')->update_or_create(
610 {
611 artist => 'Massive Attack',
612 title => 'Mezzanine',
613 year => 1998,
614 },
615 { key => 'artist_title' }
616 );
617
618If no C<key> is specified, it searches on all unique constraints defined on the
619source, including the primary key.
620
621If the C<key> is specified as C<primary>, search only on the primary key.
622
a33df5d4 623See also L</find> and L</find_or_create>.
624
87f0da6a 625=cut
626
627sub update_or_create {
628 my $self = shift;
629
630 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
631 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
632
701da8c4 633 my %unique_constraints = $self->result_source->unique_constraints;
87f0da6a 634 my @constraint_names = (exists $attrs->{key}
635 ? ($attrs->{key})
636 : keys %unique_constraints);
637
638 my @unique_hashes;
639 foreach my $name (@constraint_names) {
640 my @unique_cols = @{ $unique_constraints{$name} };
641 my %unique_hash =
642 map { $_ => $hash->{$_} }
643 grep { exists $hash->{$_} }
644 @unique_cols;
645
646 push @unique_hashes, \%unique_hash
647 if (scalar keys %unique_hash == scalar @unique_cols);
648 }
649
650 my $row;
651 if (@unique_hashes) {
652 $row = $self->search(\@unique_hashes, { rows => 1 })->first;
653 if ($row) {
654 $row->set_columns($hash);
655 $row->update;
656 }
657 }
658
659 unless ($row) {
660 $row = $self->create($hash);
661 }
662
663 return $row;
664}
665
701da8c4 666=head2 throw_exception
667
668See Schema's throw_exception
669
670=cut
671
672sub throw_exception {
673 my $self=shift;
674 $self->result_source->schema->throw_exception(@_);
675}
676
40dbc108 677=head1 ATTRIBUTES
076652e8 678
a33df5d4 679The resultset takes various attributes that modify its behavior. Here's an
680overview of them:
bfab575a 681
682=head2 order_by
076652e8 683
a33df5d4 684Which column(s) to order the results by. This is currently passed through
685directly to SQL, so you can give e.g. C<foo DESC> for a descending order.
076652e8 686
976f3686 687=head2 cols (arrayref)
688
a33df5d4 689Shortcut to request a particular set of columns to be retrieved. Adds
690C<me.> onto the start of any column without a C<.> in it and sets C<select>
691from that, then auto-populates C<as> from C<select> as normal.
976f3686 692
693=head2 select (arrayref)
694
4a28c340 695Indicates which columns should be selected from the storage. You can use
696column names, or in the case of RDBMS back ends, function or stored procedure
697names:
698
699 $rs = $schema->resultset('Foo')->search(
700 {},
701 {
cf7b40ed 702 select => [
4a28c340 703 'column_name',
704 { count => 'column_to_count' },
705 { sum => 'column_to_sum' }
cf7b40ed 706 ]
4a28c340 707 }
708 );
709
710When you use function/stored procedure names and do not supply an C<as>
711attribute, the column names returned are storage-dependent. E.g. MySQL would
712return a column named C<count(column_to_count)> in the above example.
976f3686 713
714=head2 as (arrayref)
076652e8 715
4a28c340 716Indicates column names for object inflation. This is used in conjunction with
717C<select>, usually when C<select> contains one or more function or stored
718procedure names:
719
720 $rs = $schema->resultset('Foo')->search(
721 {},
722 {
cf7b40ed 723 select => [
4a28c340 724 'column1',
725 { count => 'column2' }
cf7b40ed 726 ],
4a28c340 727 as => [qw/ column1 column2_count /]
728 }
729 );
730
731 my $foo = $rs->first(); # get the first Foo
732
733If the object against which the search is performed already has an accessor
734matching a column name specified in C<as>, the value can be retrieved using
735the accessor as normal:
736
737 my $column1 = $foo->column1();
738
739If on the other hand an accessor does not exist in the object, you need to
740use C<get_column> instead:
741
742 my $column2_count = $foo->get_column('column2_count');
743
744You can create your own accessors if required - see
745L<DBIx::Class::Manual::Cookbook> for details.
ee38fa40 746
bfab575a 747=head2 join
ee38fa40 748
a33df5d4 749Contains a list of relationships that should be joined for this query. For
750example:
751
752 # Get CDs by Nine Inch Nails
753 my $rs = $schema->resultset('CD')->search(
754 { 'artist.name' => 'Nine Inch Nails' },
755 { join => 'artist' }
756 );
757
758Can also contain a hash reference to refer to the other relation's relations.
759For example:
760
761 package MyApp::Schema::Track;
762 use base qw/DBIx::Class/;
763 __PACKAGE__->table('track');
764 __PACKAGE__->add_columns(qw/trackid cd position title/);
765 __PACKAGE__->set_primary_key('trackid');
766 __PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
767 1;
768
769 # In your application
770 my $rs = $schema->resultset('Artist')->search(
771 { 'track.title' => 'Teardrop' },
772 {
773 join => { cd => 'track' },
774 order_by => 'artist.name',
775 }
776 );
777
ae1c90a1 778If you want to fetch columns from related tables as well, see C<prefetch>
779below.
ee38fa40 780
ae1c90a1 781=head2 prefetch arrayref/hashref
ee38fa40 782
ae1c90a1 783Contains one or more relationships that should be fetched along with the main
bfab575a 784query (when they are accessed afterwards they will have already been
a33df5d4 785"prefetched"). This is useful for when you know you will need the related
ae1c90a1 786objects, because it saves at least one query:
787
788 my $rs = $schema->resultset('Tag')->search(
789 {},
790 {
791 prefetch => {
792 cd => 'artist'
793 }
794 }
795 );
796
797The initial search results in SQL like the following:
798
799 SELECT tag.*, cd.*, artist.* FROM tag
800 JOIN cd ON tag.cd = cd.cdid
801 JOIN artist ON cd.artist = artist.artistid
802
803L<DBIx::Class> has no need to go back to the database when we access the
804C<cd> or C<artist> relationships, which saves us two SQL statements in this
805case.
806
807Any prefetched relationship will be joined automatically, so there is no need
808for a C<join> attribute in the above search.
809
810C<prefetch> can be used with the following relationship types: C<belongs_to>,
811C<has_one>.
ee38fa40 812
4a28c340 813=head2 from (arrayref)
ee38fa40 814
4a28c340 815The C<from> attribute gives you manual control over the C<FROM> clause of SQL
816statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
817clauses.
ee38fa40 818
a33df5d4 819NOTE: Use this on your own risk. This allows you to shoot off your foot!
4a28c340 820C<join> will usually do what you need and it is strongly recommended that you
821avoid using C<from> unless you cannot achieve the desired result using C<join>.
822
823In simple terms, C<from> works as follows:
824
825 [
826 { <alias> => <table>, -join-type => 'inner|left|right' }
827 [] # nested JOIN (optional)
828 { <table.column> = <foreign_table.foreign_key> }
829 ]
830
831 JOIN
832 <alias> <table>
833 [JOIN ...]
834 ON <table.column> = <foreign_table.foreign_key>
835
836An easy way to follow the examples below is to remember the following:
837
838 Anything inside "[]" is a JOIN
839 Anything inside "{}" is a condition for the enclosing JOIN
840
841The following examples utilize a "person" table in a family tree application.
842In order to express parent->child relationships, this table is self-joined:
843
844 # Person->belongs_to('father' => 'Person');
845 # Person->belongs_to('mother' => 'Person');
846
847C<from> can be used to nest joins. Here we return all children with a father,
848then search against all mothers of those children:
849
850 $rs = $schema->resultset('Person')->search(
851 {},
852 {
853 alias => 'mother', # alias columns in accordance with "from"
854 from => [
855 { mother => 'person' },
856 [
857 [
858 { child => 'person' },
859 [
860 { father => 'person' },
861 { 'father.person_id' => 'child.father_id' }
862 ]
863 ],
864 { 'mother.person_id' => 'child.mother_id' }
865 ],
866 ]
867 },
868 );
869
870 # Equivalent SQL:
871 # SELECT mother.* FROM person mother
872 # JOIN (
873 # person child
874 # JOIN person father
875 # ON ( father.person_id = child.father_id )
876 # )
877 # ON ( mother.person_id = child.mother_id )
878
879The type of any join can be controlled manually. To search against only people
880with a father in the person table, we could explicitly use C<INNER JOIN>:
881
882 $rs = $schema->resultset('Person')->search(
883 {},
884 {
885 alias => 'child', # alias columns in accordance with "from"
886 from => [
887 { child => 'person' },
888 [
889 { father => 'person', -join-type => 'inner' },
890 { 'father.id' => 'child.father_id' }
891 ],
892 ]
893 },
894 );
895
896 # Equivalent SQL:
897 # SELECT child.* FROM person child
898 # INNER JOIN person father ON child.father_id = father.id
ee38fa40 899
bfab575a 900=head2 page
076652e8 901
a33df5d4 902For a paged resultset, specifies which page to retrieve. Leave unset
bfab575a 903for an unpaged resultset.
076652e8 904
bfab575a 905=head2 rows
076652e8 906
4a28c340 907For a paged resultset, how many rows per page:
908
909 rows => 10
910
911Can also be used to simulate an SQL C<LIMIT>.
076652e8 912
a33df5d4 913=head2 group_by (arrayref)
54540863 914
4a28c340 915A arrayref of columns to group by. Can include columns of joined tables. Note
916note that L</count> doesn't work on grouped resultsets.
54540863 917
675ce4a6 918 group_by => [qw/ column1 column2 ... /]
919
54540863 920=head2 distinct
921
a33df5d4 922Set to 1 to group by all columns.
923
924For more examples of using these attributes, see
925L<DBIx::Class::Manual::Cookbook>.
54540863 926
bfab575a 927=cut
076652e8 928
89c0a5a2 9291;