Moved DBI and DBD::SQLite to build_requires
[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];
6aeb9185 376 unless (defined $self->{count}) {
15c382be 377 my $group_by;
378 my $select = { 'count' => '*' };
379 if( $group_by = delete $self->{attrs}{group_by} ) {
380 my @distinct = @$group_by;
381 # todo: try CONCAT for multi-column pk
382 my @pk = $self->result_source->primary_columns;
383 if( scalar(@pk) == 1 ) {
384 my $pk = shift(@pk);
385 my $alias = $self->{attrs}{alias};
386 my $re = qr/^($alias\.)?$pk$/;
387 foreach my $column ( @$group_by ) {
388 if( $column =~ $re ) {
389 @distinct = ( $column );
390 last;
391 }
392 }
393 }
394
395 $select = { count => { 'distinct' => \@distinct } };
396 #use Data::Dumper; die Dumper $select;
397 }
398
976f3686 399 my $attrs = { %{ $self->{attrs} },
15c382be 400 select => $select,
54540863 401 as => [ 'count' ] };
ea20d0fd 402 # offset, order by and page are not needed to count. record_filter is cdbi
403 delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
3c5b25c5 404
701da8c4 405 ($self->{count}) = (ref $self)->new($self->result_source, $attrs)->cursor->next;
15c382be 406 $self->{attrs}{group_by} = $group_by;
3c5b25c5 407 }
408 return 0 unless $self->{count};
6aeb9185 409 my $count = $self->{count};
410 $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
411 $count = $self->{attrs}{rows} if
412 ($self->{attrs}{rows} && $self->{attrs}{rows} < $count);
413 return $count;
89c0a5a2 414}
415
bfab575a 416=head2 count_literal
6009260a 417
a33df5d4 418Calls L</search_literal> with the passed arguments, then L</count>.
6009260a 419
420=cut
421
422sub count_literal { shift->search_literal(@_)->count; }
423
bfab575a 424=head2 all
ee38fa40 425
bfab575a 426Returns all elements in the resultset. Called implictly if the resultset
427is returned in list context.
ee38fa40 428
429=cut
430
89c0a5a2 431sub all {
432 my ($self) = @_;
c7ce65e6 433 return map { $self->_construct_object(@$_); }
73f58123 434 $self->cursor->all;
89c0a5a2 435}
436
bfab575a 437=head2 reset
ee38fa40 438
bfab575a 439Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 440
441=cut
442
89c0a5a2 443sub reset {
444 my ($self) = @_;
73f58123 445 $self->cursor->reset;
89c0a5a2 446 return $self;
447}
448
bfab575a 449=head2 first
ee38fa40 450
bfab575a 451Resets the resultset and returns the first element.
ee38fa40 452
453=cut
454
89c0a5a2 455sub first {
456 return $_[0]->reset->next;
457}
458
c01ab172 459=head2 update(\%values)
460
a33df5d4 461Sets the specified columns in the resultset to the supplied values.
c01ab172 462
463=cut
464
465sub update {
466 my ($self, $values) = @_;
701da8c4 467 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
468 return $self->result_source->storage->update(
469 $self->result_source->from, $values, $self->{cond});
c01ab172 470}
471
472=head2 update_all(\%values)
473
a33df5d4 474Fetches all objects and updates them one at a time. Note that C<update_all>
475will run cascade triggers while L</update> will not.
c01ab172 476
477=cut
478
479sub update_all {
480 my ($self, $values) = @_;
701da8c4 481 $self->throw_exception("Values for update must be a hash") unless ref $values eq 'HASH';
c01ab172 482 foreach my $obj ($self->all) {
483 $obj->set_columns($values)->update;
484 }
485 return 1;
486}
487
bfab575a 488=head2 delete
ee38fa40 489
c01ab172 490Deletes the contents of the resultset from its result source.
ee38fa40 491
492=cut
493
28927b50 494sub delete {
89c0a5a2 495 my ($self) = @_;
701da8c4 496 $self->result_source->storage->delete($self->result_source->from, $self->{cond});
89c0a5a2 497 return 1;
498}
499
c01ab172 500=head2 delete_all
501
a33df5d4 502Fetches all objects and deletes them one at a time. Note that C<delete_all>
503will run cascade triggers while L</delete> will not.
c01ab172 504
505=cut
506
507sub delete_all {
508 my ($self) = @_;
509 $_->delete for $self->all;
510 return 1;
511}
28927b50 512
bfab575a 513=head2 pager
ee38fa40 514
515Returns a L<Data::Page> object for the current resultset. Only makes
a33df5d4 516sense for queries with a C<page> attribute.
ee38fa40 517
518=cut
519
3c5b25c5 520sub pager {
521 my ($self) = @_;
522 my $attrs = $self->{attrs};
701da8c4 523 $self->throw_exception("Can't create pager for non-paged rs") unless $self->{page};
6aeb9185 524 $attrs->{rows} ||= 10;
525 $self->count;
526 return $self->{pager} ||= Data::Page->new(
93b004d3 527 $self->{count}, $attrs->{rows}, $self->{page});
3c5b25c5 528}
529
bfab575a 530=head2 page($page_num)
ee38fa40 531
bfab575a 532Returns a new resultset for the specified page.
ee38fa40 533
534=cut
535
3c5b25c5 536sub page {
537 my ($self, $page) = @_;
6aeb9185 538 my $attrs = { %{$self->{attrs}} };
3c5b25c5 539 $attrs->{page} = $page;
701da8c4 540 return (ref $self)->new($self->result_source, $attrs);
fea3d045 541}
542
543=head2 new_result(\%vals)
544
87f0da6a 545Creates a result in the resultset's result class.
fea3d045 546
547=cut
548
549sub new_result {
550 my ($self, $values) = @_;
701da8c4 551 $self->throw_exception( "new_result needs a hash" )
fea3d045 552 unless (ref $values eq 'HASH');
701da8c4 553 $self->throw_exception( "Can't abstract implicit construct, condition not a hash" )
fea3d045 554 if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
555 my %new = %$values;
556 my $alias = $self->{attrs}{alias};
557 foreach my $key (keys %{$self->{cond}||{}}) {
558 $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:$alias\.)?([^\.]+)$/);
559 }
701da8c4 560 my $obj = $self->result_source->result_class->new(\%new);
561 $obj->result_source($self->result_source) if $obj->can('result_source');
097d3227 562 $obj;
fea3d045 563}
564
565=head2 create(\%vals)
566
87f0da6a 567Inserts a record into the resultset and returns the object.
fea3d045 568
a33df5d4 569Effectively a shortcut for C<< ->new_result(\%vals)->insert >>.
fea3d045 570
571=cut
572
573sub create {
574 my ($self, $attrs) = @_;
701da8c4 575 $self->throw_exception( "create needs a hashref" ) unless ref $attrs eq 'HASH';
fea3d045 576 return $self->new_result($attrs)->insert;
3c5b25c5 577}
578
87f0da6a 579=head2 find_or_create(\%vals, \%attrs?)
580
581 $class->find_or_create({ key => $val, ... });
c2b15ecc 582
c2b15ecc 583Searches for a record matching the search condition; if it doesn't find one,
cf7b40ed 584creates one and returns that instead.
87f0da6a 585
87f0da6a 586 my $cd = $schema->resultset('CD')->find_or_create({
587 cdid => 5,
588 artist => 'Massive Attack',
589 title => 'Mezzanine',
590 year => 2005,
591 });
592
593Also takes an optional C<key> attribute, to search by a specific key or unique
594constraint. For example:
595
596 my $cd = $schema->resultset('CD')->find_or_create(
597 {
598 artist => 'Massive Attack',
599 title => 'Mezzanine',
600 },
601 { key => 'artist_title' }
602 );
603
604See also L</find> and L</update_or_create>.
605
c2b15ecc 606=cut
607
608sub find_or_create {
609 my $self = shift;
87f0da6a 610 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
611 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
612 my $exists = $self->find($hash, $attrs);
c2b15ecc 613 return defined($exists) ? $exists : $self->create($hash);
614}
615
87f0da6a 616=head2 update_or_create
617
618 $class->update_or_create({ key => $val, ... });
619
620First, search for an existing row matching one of the unique constraints
621(including the primary key) on the source of this resultset. If a row is
622found, update it with the other given column values. Otherwise, create a new
623row.
624
625Takes an optional C<key> attribute to search on a specific unique constraint.
626For example:
627
628 # In your application
629 my $cd = $schema->resultset('CD')->update_or_create(
630 {
631 artist => 'Massive Attack',
632 title => 'Mezzanine',
633 year => 1998,
634 },
635 { key => 'artist_title' }
636 );
637
638If no C<key> is specified, it searches on all unique constraints defined on the
639source, including the primary key.
640
641If the C<key> is specified as C<primary>, search only on the primary key.
642
a33df5d4 643See also L</find> and L</find_or_create>.
644
87f0da6a 645=cut
646
647sub update_or_create {
648 my $self = shift;
649
650 my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
651 my $hash = ref $_[0] eq "HASH" ? shift : {@_};
652
701da8c4 653 my %unique_constraints = $self->result_source->unique_constraints;
87f0da6a 654 my @constraint_names = (exists $attrs->{key}
655 ? ($attrs->{key})
656 : keys %unique_constraints);
657
658 my @unique_hashes;
659 foreach my $name (@constraint_names) {
660 my @unique_cols = @{ $unique_constraints{$name} };
661 my %unique_hash =
662 map { $_ => $hash->{$_} }
663 grep { exists $hash->{$_} }
664 @unique_cols;
665
666 push @unique_hashes, \%unique_hash
667 if (scalar keys %unique_hash == scalar @unique_cols);
668 }
669
670 my $row;
671 if (@unique_hashes) {
672 $row = $self->search(\@unique_hashes, { rows => 1 })->first;
673 if ($row) {
674 $row->set_columns($hash);
675 $row->update;
676 }
677 }
678
679 unless ($row) {
680 $row = $self->create($hash);
681 }
682
683 return $row;
684}
685
701da8c4 686=head2 throw_exception
687
688See Schema's throw_exception
689
690=cut
691
692sub throw_exception {
693 my $self=shift;
694 $self->result_source->schema->throw_exception(@_);
695}
696
40dbc108 697=head1 ATTRIBUTES
076652e8 698
a33df5d4 699The resultset takes various attributes that modify its behavior. Here's an
700overview of them:
bfab575a 701
702=head2 order_by
076652e8 703
a33df5d4 704Which column(s) to order the results by. This is currently passed through
705directly to SQL, so you can give e.g. C<foo DESC> for a descending order.
076652e8 706
976f3686 707=head2 cols (arrayref)
708
a33df5d4 709Shortcut to request a particular set of columns to be retrieved. Adds
710C<me.> onto the start of any column without a C<.> in it and sets C<select>
711from that, then auto-populates C<as> from C<select> as normal.
976f3686 712
713=head2 select (arrayref)
714
4a28c340 715Indicates which columns should be selected from the storage. You can use
716column names, or in the case of RDBMS back ends, function or stored procedure
717names:
718
719 $rs = $schema->resultset('Foo')->search(
720 {},
721 {
cf7b40ed 722 select => [
4a28c340 723 'column_name',
724 { count => 'column_to_count' },
725 { sum => 'column_to_sum' }
cf7b40ed 726 ]
4a28c340 727 }
728 );
729
730When you use function/stored procedure names and do not supply an C<as>
731attribute, the column names returned are storage-dependent. E.g. MySQL would
732return a column named C<count(column_to_count)> in the above example.
976f3686 733
734=head2 as (arrayref)
076652e8 735
4a28c340 736Indicates column names for object inflation. This is used in conjunction with
737C<select>, usually when C<select> contains one or more function or stored
738procedure names:
739
740 $rs = $schema->resultset('Foo')->search(
741 {},
742 {
cf7b40ed 743 select => [
4a28c340 744 'column1',
745 { count => 'column2' }
cf7b40ed 746 ],
4a28c340 747 as => [qw/ column1 column2_count /]
748 }
749 );
750
751 my $foo = $rs->first(); # get the first Foo
752
753If the object against which the search is performed already has an accessor
754matching a column name specified in C<as>, the value can be retrieved using
755the accessor as normal:
756
757 my $column1 = $foo->column1();
758
759If on the other hand an accessor does not exist in the object, you need to
760use C<get_column> instead:
761
762 my $column2_count = $foo->get_column('column2_count');
763
764You can create your own accessors if required - see
765L<DBIx::Class::Manual::Cookbook> for details.
ee38fa40 766
bfab575a 767=head2 join
ee38fa40 768
a33df5d4 769Contains a list of relationships that should be joined for this query. For
770example:
771
772 # Get CDs by Nine Inch Nails
773 my $rs = $schema->resultset('CD')->search(
774 { 'artist.name' => 'Nine Inch Nails' },
775 { join => 'artist' }
776 );
777
778Can also contain a hash reference to refer to the other relation's relations.
779For example:
780
781 package MyApp::Schema::Track;
782 use base qw/DBIx::Class/;
783 __PACKAGE__->table('track');
784 __PACKAGE__->add_columns(qw/trackid cd position title/);
785 __PACKAGE__->set_primary_key('trackid');
786 __PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
787 1;
788
789 # In your application
790 my $rs = $schema->resultset('Artist')->search(
791 { 'track.title' => 'Teardrop' },
792 {
793 join => { cd => 'track' },
794 order_by => 'artist.name',
795 }
796 );
797
ae1c90a1 798If you want to fetch columns from related tables as well, see C<prefetch>
799below.
ee38fa40 800
ae1c90a1 801=head2 prefetch arrayref/hashref
ee38fa40 802
ae1c90a1 803Contains one or more relationships that should be fetched along with the main
bfab575a 804query (when they are accessed afterwards they will have already been
a33df5d4 805"prefetched"). This is useful for when you know you will need the related
ae1c90a1 806objects, because it saves at least one query:
807
808 my $rs = $schema->resultset('Tag')->search(
809 {},
810 {
811 prefetch => {
812 cd => 'artist'
813 }
814 }
815 );
816
817The initial search results in SQL like the following:
818
819 SELECT tag.*, cd.*, artist.* FROM tag
820 JOIN cd ON tag.cd = cd.cdid
821 JOIN artist ON cd.artist = artist.artistid
822
823L<DBIx::Class> has no need to go back to the database when we access the
824C<cd> or C<artist> relationships, which saves us two SQL statements in this
825case.
826
827Any prefetched relationship will be joined automatically, so there is no need
828for a C<join> attribute in the above search.
829
830C<prefetch> can be used with the following relationship types: C<belongs_to>,
831C<has_one>.
ee38fa40 832
4a28c340 833=head2 from (arrayref)
ee38fa40 834
4a28c340 835The C<from> attribute gives you manual control over the C<FROM> clause of SQL
836statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
837clauses.
ee38fa40 838
a33df5d4 839NOTE: Use this on your own risk. This allows you to shoot off your foot!
4a28c340 840C<join> will usually do what you need and it is strongly recommended that you
841avoid using C<from> unless you cannot achieve the desired result using C<join>.
842
843In simple terms, C<from> works as follows:
844
845 [
846 { <alias> => <table>, -join-type => 'inner|left|right' }
847 [] # nested JOIN (optional)
848 { <table.column> = <foreign_table.foreign_key> }
849 ]
850
851 JOIN
852 <alias> <table>
853 [JOIN ...]
854 ON <table.column> = <foreign_table.foreign_key>
855
856An easy way to follow the examples below is to remember the following:
857
858 Anything inside "[]" is a JOIN
859 Anything inside "{}" is a condition for the enclosing JOIN
860
861The following examples utilize a "person" table in a family tree application.
862In order to express parent->child relationships, this table is self-joined:
863
864 # Person->belongs_to('father' => 'Person');
865 # Person->belongs_to('mother' => 'Person');
866
867C<from> can be used to nest joins. Here we return all children with a father,
868then search against all mothers of those children:
869
870 $rs = $schema->resultset('Person')->search(
871 {},
872 {
873 alias => 'mother', # alias columns in accordance with "from"
874 from => [
875 { mother => 'person' },
876 [
877 [
878 { child => 'person' },
879 [
880 { father => 'person' },
881 { 'father.person_id' => 'child.father_id' }
882 ]
883 ],
884 { 'mother.person_id' => 'child.mother_id' }
885 ],
886 ]
887 },
888 );
889
890 # Equivalent SQL:
891 # SELECT mother.* FROM person mother
892 # JOIN (
893 # person child
894 # JOIN person father
895 # ON ( father.person_id = child.father_id )
896 # )
897 # ON ( mother.person_id = child.mother_id )
898
899The type of any join can be controlled manually. To search against only people
900with a father in the person table, we could explicitly use C<INNER JOIN>:
901
902 $rs = $schema->resultset('Person')->search(
903 {},
904 {
905 alias => 'child', # alias columns in accordance with "from"
906 from => [
907 { child => 'person' },
908 [
909 { father => 'person', -join-type => 'inner' },
910 { 'father.id' => 'child.father_id' }
911 ],
912 ]
913 },
914 );
915
916 # Equivalent SQL:
917 # SELECT child.* FROM person child
918 # INNER JOIN person father ON child.father_id = father.id
ee38fa40 919
bfab575a 920=head2 page
076652e8 921
a33df5d4 922For a paged resultset, specifies which page to retrieve. Leave unset
bfab575a 923for an unpaged resultset.
076652e8 924
bfab575a 925=head2 rows
076652e8 926
4a28c340 927For a paged resultset, how many rows per page:
928
929 rows => 10
930
931Can also be used to simulate an SQL C<LIMIT>.
076652e8 932
a33df5d4 933=head2 group_by (arrayref)
54540863 934
4a28c340 935A arrayref of columns to group by. Can include columns of joined tables. Note
936note that L</count> doesn't work on grouped resultsets.
54540863 937
675ce4a6 938 group_by => [qw/ column1 column2 ... /]
939
54540863 940=head2 distinct
941
a33df5d4 942Set to 1 to group by all columns.
943
944For more examples of using these attributes, see
945L<DBIx::Class::Manual::Cookbook>.
54540863 946
bfab575a 947=cut
076652e8 948
89c0a5a2 9491;