f2d0fd94026be050728207ef5efa1cd8fc0fac75
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / ResultSet.pm
1 package DBIx::Class::ResultSet;
2
3 use strict;
4 use warnings;
5 use overload
6         '0+'     => \&count,
7         'bool'   => sub { 1; },
8         fallback => 1;
9 use Data::Page;
10 use Storable;
11 use Scalar::Util qw/weaken/;
12
13 use DBIx::Class::ResultSetColumn;
14 use base qw/DBIx::Class/;
15 __PACKAGE__->load_components(qw/AccessorGroup/);
16 __PACKAGE__->mk_group_accessors('simple' => qw/result_source result_class/);
17
18 =head1 NAME
19
20 DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
21
22 =head1 SYNOPSIS
23
24   my $rs   = $schema->resultset('User')->search(registered => 1);
25   my @rows = $schema->resultset('CD')->search(year => 2005);
26
27 =head1 DESCRIPTION
28
29 The resultset is also known as an iterator. It is responsible for handling
30 queries that may return an arbitrary number of rows, e.g. via L</search>
31 or a C<has_many> relationship.
32
33 In the examples below, the following table classes are used:
34
35   package MyApp::Schema::Artist;
36   use base qw/DBIx::Class/;
37   __PACKAGE__->load_components(qw/Core/);
38   __PACKAGE__->table('artist');
39   __PACKAGE__->add_columns(qw/artistid name/);
40   __PACKAGE__->set_primary_key('artistid');
41   __PACKAGE__->has_many(cds => 'MyApp::Schema::CD');
42   1;
43
44   package MyApp::Schema::CD;
45   use base qw/DBIx::Class/;
46   __PACKAGE__->load_components(qw/Core/);
47   __PACKAGE__->table('cd');
48   __PACKAGE__->add_columns(qw/cdid artist title year/);
49   __PACKAGE__->set_primary_key('cdid');
50   __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
51   1;
52
53 =head1 METHODS
54
55 =head2 new
56
57 =over 4
58
59 =item Arguments: $source, \%$attrs
60
61 =item Return Value: $rs
62
63 =back
64
65 The resultset constructor. Takes a source object (usually a
66 L<DBIx::Class::ResultSourceProxy::Table>) and an attribute hash (see
67 L</ATTRIBUTES> below).  Does not perform any queries -- these are
68 executed as needed by the other methods.
69
70 Generally you won't need to construct a resultset manually.  You'll
71 automatically get one from e.g. a L</search> called in scalar context:
72
73   my $rs = $schema->resultset('CD')->search({ title => '100th Window' });
74
75 IMPORTANT: If called on an object, proxies to new_result instead so
76
77   my $cd = $schema->resultset('CD')->new({ title => 'Spoon' });
78
79 will return a CD object, not a ResultSet.
80
81 =cut
82
83 sub new {
84   my $class = shift;
85   return $class->new_result(@_) if ref $class;
86   
87   my ($source, $attrs) = @_;
88   weaken $source;
89   $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } };
90   #use Data::Dumper; warn Dumper($attrs);
91   my $alias = ($attrs->{alias} ||= 'me');
92   
93   $attrs->{columns} ||= delete $attrs->{cols} if $attrs->{cols};
94   delete $attrs->{as} if $attrs->{columns};
95   $attrs->{columns} ||= [ $source->columns ] unless $attrs->{select};
96   $attrs->{select} = [
97     map { m/\./ ? $_ : "${alias}.$_" } @{delete $attrs->{columns}}
98   ] if $attrs->{columns};
99   $attrs->{as} ||= [
100     map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}}
101   ];
102   if (my $include = delete $attrs->{include_columns}) {
103     push(@{$attrs->{select}}, @$include);
104     push(@{$attrs->{as}}, map { m/([^.]+)$/; $1; } @$include);
105   }
106   #use Data::Dumper; warn Dumper(@{$attrs}{qw/select as/});
107
108   $attrs->{from} ||= [ { $alias => $source->from } ];
109   $attrs->{seen_join} ||= {};
110   my %seen;
111   if (my $join = delete $attrs->{join}) {
112     foreach my $j (ref $join eq 'ARRAY' ? @$join : ($join)) {
113       if (ref $j eq 'HASH') {
114         $seen{$_} = 1 foreach keys %$j;
115       } else {
116         $seen{$j} = 1;
117       }
118     }
119     push(@{$attrs->{from}}, $source->resolve_join(
120       $join, $attrs->{alias}, $attrs->{seen_join})
121     );
122   }
123   
124   $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
125   $attrs->{order_by} = [ $attrs->{order_by} ] if
126     $attrs->{order_by} and !ref($attrs->{order_by});
127   $attrs->{order_by} ||= [];
128
129   my $collapse = $attrs->{collapse} || {};
130   if (my $prefetch = delete $attrs->{prefetch}) {
131     my @pre_order;
132     foreach my $p (ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch)) {
133       if ( ref $p eq 'HASH' ) {
134         foreach my $key (keys %$p) {
135           push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
136             unless $seen{$key};
137         }
138       } else {
139         push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
140             unless $seen{$p};
141       }
142       my @prefetch = $source->resolve_prefetch(
143            $p, $attrs->{alias}, {}, \@pre_order, $collapse);
144       push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
145       push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
146     }
147     push(@{$attrs->{order_by}}, @pre_order);
148   }
149   $attrs->{collapse} = $collapse;
150 #  use Data::Dumper; warn Dumper($collapse) if keys %{$collapse};
151
152   if ($attrs->{page}) {
153     $attrs->{rows} ||= 10;
154     $attrs->{offset} ||= 0;
155     $attrs->{offset} += ($attrs->{rows} * ($attrs->{page} - 1));
156   }
157
158   bless {
159     result_source => $source,
160     result_class => $attrs->{result_class} || $source->result_class,
161     cond => $attrs->{where},
162     from => $attrs->{from},
163     collapse => $collapse,
164     count => undef,
165     page => delete $attrs->{page},
166     pager => undef,
167     attrs => $attrs
168   }, $class;
169 }
170
171 =head2 search
172
173 =over 4
174
175 =item Arguments: $cond, \%attrs?
176
177 =item Return Value: $resultset (scalar context), @row_objs (list context)
178
179 =back
180
181   my @cds    = $cd_rs->search({ year => 2001 }); # "... WHERE year = 2001"
182   my $new_rs = $cd_rs->search({ year => 2005 });
183
184   my $new_rs = $cd_rs->search([ { year => 2005 }, { year => 2004 } ]);
185                  # year = 2005 OR year = 2004
186
187 If you need to pass in additional attributes but no additional condition,
188 call it as C<search(undef, \%attrs)>.
189
190   # "SELECT name, artistid FROM $artist_table"
191   my @all_artists = $schema->resultset('Artist')->search(undef, {
192     columns => [qw/name artistid/],
193   });
194
195 =cut
196
197 sub search {
198   my $self = shift;
199     
200   my $attrs = { %{$self->{attrs}} };
201   my $having = delete $attrs->{having};
202   $attrs = { %$attrs, %{ pop(@_) } } if @_ > 1 and ref $_[$#_] eq 'HASH';
203
204   my $where = (@_
205                 ? ((@_ == 1 || ref $_[0] eq "HASH")
206                     ? shift
207                     : ((@_ % 2)
208                         ? $self->throw_exception(
209                             "Odd number of arguments to search")
210                         : {@_}))
211                 : undef());
212   if (defined $where) {
213     $attrs->{where} = (defined $attrs->{where}
214               ? { '-and' =>
215                   [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
216                       $where, $attrs->{where} ] }
217               : $where);
218   }
219
220   if (defined $having) {
221     $attrs->{having} = (defined $attrs->{having}
222               ? { '-and' =>
223                   [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
224                       $having, $attrs->{having} ] }
225               : $having);
226   }
227
228   my $rs = (ref $self)->new($self->result_source, $attrs);
229
230   my $rows = $self->get_cache;
231   if( @{$rows} ) {
232     $rs->set_cache($rows);
233   }
234   
235   return (wantarray ? $rs->all : $rs);
236 }
237
238 =head2 search_literal
239
240 =over 4
241
242 =item Arguments: $sql_fragment, @bind_values
243
244 =item Return Value: $resultset (scalar context), @row_objs (list context)
245
246 =back
247
248   my @cds   = $cd_rs->search_literal('year = ? AND title = ?', qw/2001 Reload/);
249   my $newrs = $artist_rs->search_literal('name = ?', 'Metallica');
250
251 Pass a literal chunk of SQL to be added to the conditional part of the
252 resultset query.
253
254 =cut
255
256 sub search_literal {
257   my ($self, $cond, @vals) = @_;
258   my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {});
259   $attrs->{bind} = [ @{$self->{attrs}{bind}||[]}, @vals ];
260   return $self->search(\$cond, $attrs);
261 }
262
263 =head2 find
264
265 =over 4
266
267 =item Arguments: @values | \%cols, \%attrs?
268
269 =item Return Value: $row_object
270
271 =back
272
273 Finds a row based on its primary key or unique constraint. For example:
274
275   my $cd = $schema->resultset('CD')->find(5);
276
277 Also takes an optional C<key> attribute, to search by a specific key or unique
278 constraint. For example:
279
280   my $cd = $schema->resultset('CD')->find(
281     {
282       artist => 'Massive Attack',
283       title  => 'Mezzanine',
284     },
285     { key => 'artist_title' }
286   );
287
288 See also L</find_or_create> and L</update_or_create>.
289
290 =cut
291
292 sub find {
293   my ($self, @vals) = @_;
294   my $attrs = (@vals > 1 && ref $vals[$#vals] eq 'HASH' ? pop(@vals) : {});
295
296   my @cols = $self->result_source->primary_columns;
297   if (exists $attrs->{key}) {
298     my %uniq = $self->result_source->unique_constraints;
299     $self->throw_exception(
300       "Unknown key $attrs->{key} on '" . $self->result_source->name . "'"
301     ) unless exists $uniq{$attrs->{key}};
302     @cols = @{ $uniq{$attrs->{key}} };
303   }
304   #use Data::Dumper; warn Dumper($attrs, @vals, @cols);
305   $self->throw_exception(
306     "Can't find unless a primary key or unique constraint is defined"
307   ) unless @cols;
308
309   my $query;
310   if (ref $vals[0] eq 'HASH') {
311     $query = { %{$vals[0]} };
312   } elsif (@cols == @vals) {
313     $query = {};
314     @{$query}{@cols} = @vals;
315   } else {
316     $query = {@vals};
317   }
318   foreach my $key (grep { ! m/\./ } keys %$query) {
319     $query->{"$self->{attrs}{alias}.$key"} = delete $query->{$key};
320   }
321   #warn Dumper($query);
322   
323   if (keys %$attrs) {
324       my $rs = $self->search($query,$attrs);
325       return keys %{$rs->{collapse}} ? $rs->next : $rs->single;
326   } else {
327       return keys %{$self->{collapse}} ?
328         $self->search($query)->next :
329         $self->single($query);
330   }
331 }
332
333 =head2 search_related
334
335 =over 4
336
337 =item Arguments: $cond, \%attrs?
338
339 =item Return Value: $new_resultset
340
341 =back
342
343   $new_rs = $cd_rs->search_related('artist', {
344     name => 'Emo-R-Us',
345   });
346
347 Searches the specified relationship, optionally specifying a condition and
348 attributes for matching records. See L</ATTRIBUTES> for more information.
349
350 =cut
351
352 sub search_related {
353   return shift->related_resultset(shift)->search(@_);
354 }
355
356 =head2 cursor
357
358 =over 4
359
360 =item Arguments: none
361
362 =item Return Value: $cursor
363
364 =back
365
366 Returns a storage-driven cursor to the given resultset. See
367 L<DBIx::Class::Cursor> for more information.
368
369 =cut
370
371 sub cursor {
372   my ($self) = @_;
373   my $attrs = { %{$self->{attrs}} };
374   return $self->{cursor}
375     ||= $self->result_source->storage->select($self->{from}, $attrs->{select},
376           $attrs->{where},$attrs);
377 }
378
379 =head2 single
380
381 =over 4
382
383 =item Arguments: $cond?
384
385 =item Return Value: $row_object?
386
387 =back
388
389   my $cd = $schema->resultset('CD')->single({ year => 2001 });
390
391 Inflates the first result without creating a cursor if the resultset has
392 any records in it; if not returns nothing. Used by find() as an optimisation.
393
394 =cut
395
396 sub single {
397   my ($self, $where) = @_;
398   my $attrs = { %{$self->{attrs}} };
399   if ($where) {
400     if (defined $attrs->{where}) {
401       $attrs->{where} = {
402         '-and' =>
403             [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
404                $where, delete $attrs->{where} ]
405       };
406     } else {
407       $attrs->{where} = $where;
408     }
409   }
410   my @data = $self->result_source->storage->select_single(
411           $self->{from}, $attrs->{select},
412           $attrs->{where},$attrs);
413   return (@data ? $self->_construct_object(@data) : ());
414 }
415
416 =head2 get_column
417
418 =over 4
419
420 =item Arguments: $cond?
421
422 =item Return Value: $resultsetcolumn
423
424 =back
425
426   my $max_length = $rs->get_column('length')->max;
427
428 Returns a ResultSetColumn instance for $column based on $self
429
430 =cut
431
432 sub get_column {
433   my ($self, $column) = @_;
434
435   my $new = DBIx::Class::ResultSetColumn->new($self, $column);
436   return $new;
437 }
438
439 =head2 search_like
440
441 =over 4
442
443 =item Arguments: $cond, \%attrs?
444
445 =item Return Value: $resultset (scalar context), @row_objs (list context)
446
447 =back
448
449   # WHERE title LIKE '%blue%'
450   $cd_rs = $rs->search_like({ title => '%blue%'});
451
452 Performs a search, but uses C<LIKE> instead of C<=> as the condition. Note
453 that this is simply a convenience method. You most likely want to use
454 L</search> with specific operators.
455
456 For more information, see L<DBIx::Class::Manual::Cookbook>.
457
458 =cut
459
460 sub search_like {
461   my $class = shift;
462   my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
463   my $query = ref $_[0] eq 'HASH' ? { %{shift()} }: {@_};
464   $query->{$_} = { 'like' => $query->{$_} } for keys %$query;
465   return $class->search($query, { %$attrs });
466 }
467
468 =head2 slice
469
470 =over 4
471
472 =item Arguments: $first, $last
473
474 =item Return Value: $resultset (scalar context), @row_objs (list context)
475
476 =back
477
478 Returns a resultset or object list representing a subset of elements from the
479 resultset slice is called on. Indexes are from 0, i.e., to get the first
480 three records, call:
481
482   my ($one, $two, $three) = $rs->slice(0, 2);
483
484 =cut
485
486 sub slice {
487   my ($self, $min, $max) = @_;
488   my $attrs = {}; # = { %{ $self->{attrs} || {} } };
489   $attrs->{offset} = $self->{attrs}{offset} || 0;
490   $attrs->{offset} += $min;
491   $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
492   return $self->search(undef(), $attrs);
493   #my $slice = (ref $self)->new($self->result_source, $attrs);
494   #return (wantarray ? $slice->all : $slice);
495 }
496
497 =head2 next
498
499 =over 4
500
501 =item Arguments: none
502
503 =item Return Value: $result?
504
505 =back
506
507 Returns the next element in the resultset (C<undef> is there is none).
508
509 Can be used to efficiently iterate over records in the resultset:
510
511   my $rs = $schema->resultset('CD')->search;
512   while (my $cd = $rs->next) {
513     print $cd->title;
514   }
515
516 Note that you need to store the resultset object, and call C<next> on it. 
517 Calling C<< resultset('Table')->next >> repeatedly will always return the
518 first record from the resultset.
519
520 =cut
521
522 sub next {
523   my ($self) = @_;
524   if (@{$self->{all_cache} || []}) {
525     $self->{all_cache_position} ||= 0;
526     return $self->{all_cache}->[$self->{all_cache_position}++];
527   }
528   if ($self->{attrs}{cache}) {
529     $self->{all_cache_position} = 1;
530     return ($self->all)[0];
531   }
532   my @row = (exists $self->{stashed_row} ?
533                @{delete $self->{stashed_row}} :
534                $self->cursor->next
535   );
536 #  warn Dumper(\@row); use Data::Dumper;
537   return unless (@row);
538   return $self->_construct_object(@row);
539 }
540
541 sub _construct_object {
542   my ($self, @row) = @_;
543   my @as = @{ $self->{attrs}{as} };
544   
545   my $info = $self->_collapse_result(\@as, \@row);
546   
547   my $new = $self->result_class->inflate_result($self->result_source, @$info);
548   
549   $new = $self->{attrs}{record_filter}->($new)
550     if exists $self->{attrs}{record_filter};
551   return $new;
552 }
553
554 sub _collapse_result {
555   my ($self, $as, $row, $prefix) = @_;
556
557   my %const;
558
559   my @copy = @$row;
560   foreach my $this_as (@$as) {
561     my $val = shift @copy;
562     if (defined $prefix) {
563       if ($this_as =~ m/^\Q${prefix}.\E(.+)$/) {
564         my $remain = $1;
565         $remain =~ /^(?:(.*)\.)?([^.]+)$/;
566         $const{$1||''}{$2} = $val;
567       }
568     } else {
569       $this_as =~ /^(?:(.*)\.)?([^.]+)$/;
570       $const{$1||''}{$2} = $val;
571     }
572   }
573
574   my $info = [ {}, {} ];
575   foreach my $key (keys %const) {
576     if (length $key) {
577       my $target = $info;
578       my @parts = split(/\./, $key);
579       foreach my $p (@parts) {
580         $target = $target->[1]->{$p} ||= [];
581       }
582       $target->[0] = $const{$key};
583     } else {
584       $info->[0] = $const{$key};
585     }
586   }
587
588   my @collapse;
589   if (defined $prefix) {
590     @collapse = map {
591         m/^\Q${prefix}.\E(.+)$/ ? ($1) : ()
592     } keys %{$self->{collapse}}
593   } else {
594     @collapse = keys %{$self->{collapse}};
595   };
596
597   if (@collapse) {
598     my ($c) = sort { length $a <=> length $b } @collapse;
599     my $target = $info;
600     foreach my $p (split(/\./, $c)) {
601       $target = $target->[1]->{$p} ||= [];
602     }
603     my $c_prefix = (defined($prefix) ? "${prefix}.${c}" : $c);
604     my @co_key = @{$self->{collapse}{$c_prefix}};
605     my %co_check = map { ($_, $target->[0]->{$_}); } @co_key;
606     my $tree = $self->_collapse_result($as, $row, $c_prefix);
607     my (@final, @raw);
608     while ( !(grep {
609                 !defined($tree->[0]->{$_}) ||
610                 $co_check{$_} ne $tree->[0]->{$_}
611               } @co_key) ) {
612       push(@final, $tree);
613       last unless (@raw = $self->cursor->next);
614       $row = $self->{stashed_row} = \@raw;
615       $tree = $self->_collapse_result($as, $row, $c_prefix);
616       #warn Data::Dumper::Dumper($tree, $row);
617     }
618     @$target = @final;
619   }
620
621   return $info;
622 }
623
624 =head2 result_source
625
626 =over 4
627
628 =item Arguments: $result_source?
629
630 =item Return Value: $result_source
631
632 =back
633
634 An accessor for the primary ResultSource object from which this ResultSet
635 is derived.
636
637 =cut
638
639
640 =head2 count
641
642 =over 4
643
644 =item Arguments: $cond, \%attrs??
645
646 =item Return Value: $count
647
648 =back
649
650 Performs an SQL C<COUNT> with the same query as the resultset was built
651 with to find the number of elements. If passed arguments, does a search
652 on the resultset and counts the results of that.
653
654 Note: When using C<count> with C<group_by>, L<DBIX::Class> emulates C<GROUP BY>
655 using C<COUNT( DISTINCT( columns ) )>. Some databases (notably SQLite) do
656 not support C<DISTINCT> with multiple columns. If you are using such a
657 database, you should only use columns from the main table in your C<group_by>
658 clause.
659
660 =cut
661
662 sub count {
663   my $self = shift;
664   return $self->search(@_)->count if @_ and defined $_[0];
665   return scalar @{ $self->get_cache } if @{ $self->get_cache };
666
667   my $count = $self->_count;
668   return 0 unless $count;
669
670   $count -= $self->{attrs}{offset} if $self->{attrs}{offset};
671   $count = $self->{attrs}{rows} if
672     $self->{attrs}{rows} and $self->{attrs}{rows} < $count;
673   return $count;
674 }
675
676 sub _count { # Separated out so pager can get the full count
677   my $self = shift;
678   my $select = { count => '*' };
679   my $attrs = { %{ $self->{attrs} } };
680   if (my $group_by = delete $attrs->{group_by}) {
681     delete $attrs->{having};
682     my @distinct = (ref $group_by ?  @$group_by : ($group_by));
683     # todo: try CONCAT for multi-column pk
684     my @pk = $self->result_source->primary_columns;
685     if (@pk == 1) {
686       foreach my $column (@distinct) {
687         if ($column =~ qr/^(?:\Q$attrs->{alias}.\E)?$pk[0]$/) {
688           @distinct = ($column);
689           last;
690         }
691       }
692     }
693
694     $select = { count => { distinct => \@distinct } };
695     #use Data::Dumper; die Dumper $select;
696   }
697
698   $attrs->{select} = $select;
699   $attrs->{as} = [qw/count/];
700
701   # offset, order by and page are not needed to count. record_filter is cdbi
702   delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/;
703         
704   my ($count) = (ref $self)->new($self->result_source, $attrs)->cursor->next;
705   return $count;
706 }
707
708 =head2 count_literal
709
710 =over 4
711
712 =item Arguments: $sql_fragment, @bind_values
713
714 =item Return Value: $count
715
716 =back
717
718 Counts the results in a literal query. Equivalent to calling L</search_literal>
719 with the passed arguments, then L</count>.
720
721 =cut
722
723 sub count_literal { shift->search_literal(@_)->count; }
724
725 =head2 all
726
727 =over 4
728
729 =item Arguments: none
730
731 =item Return Value: @objects
732
733 =back
734
735 Returns all elements in the resultset. Called implicitly if the resultset
736 is returned in list context.
737
738 =cut
739
740 sub all {
741   my ($self) = @_;
742   return @{ $self->get_cache } if @{ $self->get_cache };
743
744   my @obj;
745
746   if (keys %{$self->{collapse}}) {
747       # Using $self->cursor->all is really just an optimisation.
748       # If we're collapsing has_many prefetches it probably makes
749       # very little difference, and this is cleaner than hacking
750       # _construct_object to survive the approach
751     $self->cursor->reset;
752     my @row = $self->cursor->next;
753     while (@row) {
754       push(@obj, $self->_construct_object(@row));
755       @row = (exists $self->{stashed_row}
756                ? @{delete $self->{stashed_row}}
757                : $self->cursor->next);
758     }
759   } else {
760     @obj = map { $self->_construct_object(@$_) } $self->cursor->all;
761   }
762
763   $self->set_cache(\@obj) if $self->{attrs}{cache};
764   return @obj;
765 }
766
767 =head2 reset
768
769 =over 4
770
771 =item Arguments: none
772
773 =item Return Value: $self
774
775 =back
776
777 Resets the resultset's cursor, so you can iterate through the elements again.
778
779 =cut
780
781 sub reset {
782   my ($self) = @_;
783   $self->{all_cache_position} = 0;
784   $self->cursor->reset;
785   return $self;
786 }
787
788 =head2 first
789
790 =over 4
791
792 =item Arguments: none
793
794 =item Return Value: $object?
795
796 =back
797
798 Resets the resultset and returns an object for the first result (if the
799 resultset returns anything).
800
801 =cut
802
803 sub first {
804   return $_[0]->reset->next;
805 }
806
807 # _cond_for_update_delete
808 #
809 # update/delete require the condition to be modified to handle
810 # the differing SQL syntax available.  This transforms the $self->{cond}
811 # appropriately, returning the new condition.
812
813 sub _cond_for_update_delete {
814   my ($self) = @_;
815   my $cond = {};
816
817   if (!ref($self->{cond})) {
818     # No-op. No condition, we're updating/deleting everything
819   }
820   elsif (ref $self->{cond} eq 'ARRAY') {
821     $cond = [
822       map {
823         my %hash;
824         foreach my $key (keys %{$_}) {
825           $key =~ /([^.]+)$/;
826           $hash{$1} = $_->{$key};
827         }
828         \%hash;
829       } @{$self->{cond}}
830     ];
831   }
832   elsif (ref $self->{cond} eq 'HASH') {
833     if ((keys %{$self->{cond}})[0] eq '-and') {
834       $cond->{-and} = [];
835
836       my @cond = @{$self->{cond}{-and}};
837       for (my $i = 0; $i < @cond - 1; $i++) {
838         my $entry = $cond[$i];
839
840         my %hash;
841         if (ref $entry eq 'HASH') {
842           foreach my $key (keys %{$entry}) {
843             $key =~ /([^.]+)$/;
844             $hash{$1} = $entry->{$key};
845           }
846         }
847         else {
848           $entry =~ /([^.]+)$/;
849           $hash{$entry} = $cond[++$i];
850         }
851
852         push @{$cond->{-and}}, \%hash;
853       }
854     }
855     else {
856       foreach my $key (keys %{$self->{cond}}) {
857         $key =~ /([^.]+)$/;
858         $cond->{$1} = $self->{cond}{$key};
859       }
860     }
861   }
862   else {
863     $self->throw_exception(
864       "Can't update/delete on resultset with condition unless hash or array"
865     );
866   }
867
868   return $cond;
869 }
870
871
872 =head2 update
873
874 =over 4
875
876 =item Arguments: \%values
877
878 =item Return Value: $storage_rv
879
880 =back
881
882 Sets the specified columns in the resultset to the supplied values in a
883 single query. Return value will be true if the update succeeded or false
884 if no records were updated; exact type of success value is storage-dependent.
885
886 =cut
887
888 sub update {
889   my ($self, $values) = @_;
890   $self->throw_exception("Values for update must be a hash")
891     unless ref $values eq 'HASH';
892
893   my $cond = $self->_cond_for_update_delete;
894
895   return $self->result_source->storage->update(
896     $self->result_source->from, $values, $cond
897   );
898 }
899
900 =head2 update_all
901
902 =over 4
903
904 =item Arguments: \%values
905
906 =item Return Value: 1
907
908 =back
909
910 Fetches all objects and updates them one at a time. Note that C<update_all>
911 will run DBIC cascade triggers, while L</update> will not.
912
913 =cut
914
915 sub update_all {
916   my ($self, $values) = @_;
917   $self->throw_exception("Values for update must be a hash")
918     unless ref $values eq 'HASH';
919   foreach my $obj ($self->all) {
920     $obj->set_columns($values)->update;
921   }
922   return 1;
923 }
924
925 =head2 delete
926
927 =over 4
928
929 =item Arguments: none
930
931 =item Return Value: 1
932
933 =back
934
935 Deletes the contents of the resultset from its result source. Note that this
936 will not run DBIC cascade triggers. See L</delete_all> if you need triggers
937 to run.
938
939 =cut
940
941 sub delete {
942   my ($self) = @_;
943   my $del = {};
944
945   my $cond = $self->_cond_for_update_delete;
946
947   $self->result_source->storage->delete($self->result_source->from, $cond);
948   return 1;
949 }
950
951 =head2 delete_all
952
953 =over 4
954
955 =item Arguments: none
956
957 =item Return Value: 1
958
959 =back
960
961 Fetches all objects and deletes them one at a time. Note that C<delete_all>
962 will run DBIC cascade triggers, while L</delete> will not.
963
964 =cut
965
966 sub delete_all {
967   my ($self) = @_;
968   $_->delete for $self->all;
969   return 1;
970 }
971
972 =head2 pager
973
974 =over 4
975
976 =item Arguments: none
977
978 =item Return Value: $pager
979
980 =back
981
982 Return Value a L<Data::Page> object for the current resultset. Only makes
983 sense for queries with a C<page> attribute.
984
985 =cut
986
987 sub pager {
988   my ($self) = @_;
989   my $attrs = $self->{attrs};
990   $self->throw_exception("Can't create pager for non-paged rs")
991     unless $self->{page};
992   $attrs->{rows} ||= 10;
993   return $self->{pager} ||= Data::Page->new(
994     $self->_count, $attrs->{rows}, $self->{page});
995 }
996
997 =head2 page
998
999 =over 4
1000
1001 =item Arguments: $page_number
1002
1003 =item Return Value: $rs
1004
1005 =back
1006
1007 Returns a resultset for the $page_number page of the resultset on which page
1008 is called, where each page contains a number of rows equal to the 'rows'
1009 attribute set on the resultset (10 by default).
1010
1011 =cut
1012
1013 sub page {
1014   my ($self, $page) = @_;
1015   my $attrs = { %{$self->{attrs}} };
1016   $attrs->{page} = $page;
1017   return (ref $self)->new($self->result_source, $attrs);
1018 }
1019
1020 =head2 new_result
1021
1022 =over 4
1023
1024 =item Arguments: \%vals
1025
1026 =item Return Value: $object
1027
1028 =back
1029
1030 Creates an object in the resultset's result class and returns it.
1031
1032 =cut
1033
1034 sub new_result {
1035   my ($self, $values) = @_;
1036   $self->throw_exception( "new_result needs a hash" )
1037     unless (ref $values eq 'HASH');
1038   $self->throw_exception(
1039     "Can't abstract implicit construct, condition not a hash"
1040   ) if ($self->{cond} && !(ref $self->{cond} eq 'HASH'));
1041   my %new = %$values;
1042   my $alias = $self->{attrs}{alias};
1043   foreach my $key (keys %{$self->{cond}||{}}) {
1044     $new{$1} = $self->{cond}{$key} if ($key =~ m/^(?:\Q${alias}.\E)?([^.]+)$/);
1045   }
1046   my $obj = $self->result_class->new(\%new);
1047   $obj->result_source($self->result_source) if $obj->can('result_source');
1048   return $obj;
1049 }
1050
1051 =head2 create
1052
1053 =over 4
1054
1055 =item Arguments: \%vals
1056
1057 =item Return Value: $object
1058
1059 =back
1060
1061 Inserts a record into the resultset and returns the object representing it.
1062
1063 Effectively a shortcut for C<< ->new_result(\%vals)->insert >>.
1064
1065 =cut
1066
1067 sub create {
1068   my ($self, $attrs) = @_;
1069   $self->throw_exception( "create needs a hashref" )
1070     unless ref $attrs eq 'HASH';
1071   return $self->new_result($attrs)->insert;
1072 }
1073
1074 =head2 find_or_create
1075
1076 =over 4
1077
1078 =item Arguments: \%vals, \%attrs?
1079
1080 =item Return Value: $object
1081
1082 =back
1083
1084   $class->find_or_create({ key => $val, ... });
1085
1086 Searches for a record matching the search condition; if it doesn't find one,
1087 creates one and returns that instead.
1088
1089   my $cd = $schema->resultset('CD')->find_or_create({
1090     cdid   => 5,
1091     artist => 'Massive Attack',
1092     title  => 'Mezzanine',
1093     year   => 2005,
1094   });
1095
1096 Also takes an optional C<key> attribute, to search by a specific key or unique
1097 constraint. For example:
1098
1099   my $cd = $schema->resultset('CD')->find_or_create(
1100     {
1101       artist => 'Massive Attack',
1102       title  => 'Mezzanine',
1103     },
1104     { key => 'artist_title' }
1105   );
1106
1107 See also L</find> and L</update_or_create>.
1108
1109 =cut
1110
1111 sub find_or_create {
1112   my $self     = shift;
1113   my $attrs    = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
1114   my $hash     = ref $_[0] eq 'HASH' ? shift : {@_};
1115   my $exists   = $self->find($hash, $attrs);
1116   return defined $exists ? $exists : $self->create($hash);
1117 }
1118
1119 =head2 update_or_create
1120
1121 =over 4
1122
1123 =item Arguments: \%col_values, { key => $unique_constraint }?
1124
1125 =item Return Value: $object
1126
1127 =back
1128
1129   $class->update_or_create({ col => $val, ... });
1130
1131 First, searches for an existing row matching one of the unique constraints
1132 (including the primary key) on the source of this resultset. If a row is
1133 found, updates it with the other given column values. Otherwise, creates a new
1134 row.
1135
1136 Takes an optional C<key> attribute to search on a specific unique constraint.
1137 For example:
1138
1139   # In your application
1140   my $cd = $schema->resultset('CD')->update_or_create(
1141     {
1142       artist => 'Massive Attack',
1143       title  => 'Mezzanine',
1144       year   => 1998,
1145     },
1146     { key => 'artist_title' }
1147   );
1148
1149 If no C<key> is specified, it searches on all unique constraints defined on the
1150 source, including the primary key.
1151
1152 If the C<key> is specified as C<primary>, it searches only on the primary key.
1153
1154 See also L</find> and L</find_or_create>.
1155
1156 =cut
1157
1158 sub update_or_create {
1159   my $self = shift;
1160   my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
1161   my $hash = ref $_[0] eq 'HASH' ? shift : {@_};
1162
1163   my %unique_constraints = $self->result_source->unique_constraints;
1164   my @constraint_names   = (exists $attrs->{key}
1165                             ? ($attrs->{key})
1166                             : keys %unique_constraints);
1167
1168   my @unique_hashes;
1169   foreach my $name (@constraint_names) {
1170     my @unique_cols = @{ $unique_constraints{$name} };
1171     my %unique_hash =
1172       map  { $_ => $hash->{$_} }
1173       grep { exists $hash->{$_} }
1174       @unique_cols;
1175
1176     push @unique_hashes, \%unique_hash
1177       if (scalar keys %unique_hash == scalar @unique_cols);
1178   }
1179
1180   if (@unique_hashes) {
1181     my $row = $self->single(\@unique_hashes);
1182     if (defined $row) {
1183       $row->set_columns($hash);
1184       $row->update;
1185       return $row;
1186     }
1187   }
1188
1189   return $self->create($hash);
1190 }
1191
1192 =head2 get_cache
1193
1194 =over 4
1195
1196 =item Arguments: none
1197
1198 =item Return Value: \@cache_objects?
1199
1200 =back
1201
1202 Gets the contents of the cache for the resultset, if the cache is set.
1203
1204 =cut
1205
1206 sub get_cache {
1207   shift->{all_cache} || [];
1208 }
1209
1210 =head2 set_cache
1211
1212 =over 4
1213
1214 =item Arguments: \@cache_objects
1215
1216 =item Return Value: \@cache_objects
1217
1218 =back
1219
1220 Sets the contents of the cache for the resultset. Expects an arrayref
1221 of objects of the same class as those produced by the resultset. Note that
1222 if the cache is set the resultset will return the cached objects rather
1223 than re-querying the database even if the cache attr is not set.
1224
1225 =cut
1226
1227 sub set_cache {
1228   my ( $self, $data ) = @_;
1229   $self->throw_exception("set_cache requires an arrayref")
1230     if ref $data ne 'ARRAY';
1231   my $result_class = $self->result_class;
1232   foreach( @$data ) {
1233     $self->throw_exception(
1234       "cannot cache object of type '$_', expected '$result_class'"
1235     ) if ref $_ ne $result_class;
1236   }
1237   $self->{all_cache} = $data;
1238 }
1239
1240 =head2 clear_cache
1241
1242 =over 4
1243
1244 =item Arguments: none
1245
1246 =item Return Value: []
1247
1248 =back
1249
1250 Clears the cache for the resultset.
1251
1252 =cut
1253
1254 sub clear_cache {
1255   shift->set_cache([]);
1256 }
1257
1258 =head2 related_resultset
1259
1260 =over 4
1261
1262 =item Arguments: $relationship_name
1263
1264 =item Return Value: $resultset
1265
1266 =back
1267
1268 Returns a related resultset for the supplied relationship name.
1269
1270   $artist_rs = $schema->resultset('CD')->related_resultset('Artist');
1271
1272 =cut
1273
1274 sub related_resultset {
1275   my ( $self, $rel ) = @_;
1276   $self->{related_resultsets} ||= {};
1277   return $self->{related_resultsets}{$rel} ||= do {
1278       #warn "fetching related resultset for rel '$rel'";
1279       my $rel_obj = $self->result_source->relationship_info($rel);
1280       $self->throw_exception(
1281         "search_related: result source '" . $self->result_source->name .
1282         "' has no such relationship ${rel}")
1283         unless $rel_obj; #die Dumper $self->{attrs};
1284
1285       my $rs = $self->search(undef, { join => $rel });
1286       my $alias = defined $rs->{attrs}{seen_join}{$rel}
1287                     && $rs->{attrs}{seen_join}{$rel} > 1
1288                   ? join('_', $rel, $rs->{attrs}{seen_join}{$rel})
1289                   : $rel;
1290
1291       $self->result_source->schema->resultset($rel_obj->{class}
1292            )->search( undef,
1293              { %{$rs->{attrs}},
1294                alias => $alias,
1295                select => undef,
1296                as => undef }
1297            );
1298   };
1299 }
1300
1301 =head2 throw_exception
1302
1303 See L<DBIx::Class::Schema/throw_exception> for details.
1304
1305 =cut
1306
1307 sub throw_exception {
1308   my $self=shift;
1309   $self->result_source->schema->throw_exception(@_);
1310 }
1311
1312 # XXX: FIXME: Attributes docs need clearing up
1313
1314 =head1 ATTRIBUTES
1315
1316 The resultset takes various attributes that modify its behavior. Here's an
1317 overview of them:
1318
1319 =head2 order_by
1320
1321 =over 4
1322
1323 =item Value: ($order_by | \@order_by)
1324
1325 =back
1326
1327 Which column(s) to order the results by. This is currently passed
1328 through directly to SQL, so you can give e.g. C<year DESC> for a
1329 descending order on the column `year'.
1330
1331 =head2 columns
1332
1333 =over 4
1334
1335 =item Value: \@columns
1336
1337 =back
1338
1339 Shortcut to request a particular set of columns to be retrieved.  Adds
1340 C<me.> onto the start of any column without a C<.> in it and sets C<select>
1341 from that, then auto-populates C<as> from C<select> as normal. (You may also
1342 use the C<cols> attribute, as in earlier versions of DBIC.)
1343
1344 =head2 include_columns
1345
1346 =over 4
1347
1348 =item Value: \@columns
1349
1350 =back
1351
1352 Shortcut to include additional columns in the returned results - for example
1353
1354   $schema->resultset('CD')->search(undef, {
1355     include_columns => ['artist.name'],
1356     join => ['artist']
1357   });
1358
1359 would return all CDs and include a 'name' column to the information
1360 passed to object inflation
1361
1362 =head2 select
1363
1364 =over 4
1365
1366 =item Value: \@select_columns
1367
1368 =back
1369
1370 Indicates which columns should be selected from the storage. You can use
1371 column names, or in the case of RDBMS back ends, function or stored procedure
1372 names:
1373
1374   $rs = $schema->resultset('Employee')->search(undef, {
1375     select => [
1376       'name',
1377       { count => 'employeeid' },
1378       { sum => 'salary' }
1379     ]
1380   });
1381
1382 When you use function/stored procedure names and do not supply an C<as>
1383 attribute, the column names returned are storage-dependent. E.g. MySQL would
1384 return a column named C<count(employeeid)> in the above example.
1385
1386 =head2 as
1387
1388 =over 4
1389
1390 =item Value: \@inflation_names
1391
1392 =back
1393
1394 Indicates column names for object inflation. This is used in conjunction with
1395 C<select>, usually when C<select> contains one or more function or stored
1396 procedure names:
1397
1398   $rs = $schema->resultset('Employee')->search(undef, {
1399     select => [
1400       'name',
1401       { count => 'employeeid' }
1402     ],
1403     as => ['name', 'employee_count'],
1404   });
1405
1406   my $employee = $rs->first(); # get the first Employee
1407
1408 If the object against which the search is performed already has an accessor
1409 matching a column name specified in C<as>, the value can be retrieved using
1410 the accessor as normal:
1411
1412   my $name = $employee->name();
1413
1414 If on the other hand an accessor does not exist in the object, you need to
1415 use C<get_column> instead:
1416
1417   my $employee_count = $employee->get_column('employee_count');
1418
1419 You can create your own accessors if required - see
1420 L<DBIx::Class::Manual::Cookbook> for details.
1421
1422 =head2 join
1423
1424 =over 4
1425
1426 =item Value: ($rel_name | \@rel_names | \%rel_names)
1427
1428 =back
1429
1430 Contains a list of relationships that should be joined for this query.  For
1431 example:
1432
1433   # Get CDs by Nine Inch Nails
1434   my $rs = $schema->resultset('CD')->search(
1435     { 'artist.name' => 'Nine Inch Nails' },
1436     { join => 'artist' }
1437   );
1438
1439 Can also contain a hash reference to refer to the other relation's relations.
1440 For example:
1441
1442   package MyApp::Schema::Track;
1443   use base qw/DBIx::Class/;
1444   __PACKAGE__->table('track');
1445   __PACKAGE__->add_columns(qw/trackid cd position title/);
1446   __PACKAGE__->set_primary_key('trackid');
1447   __PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
1448   1;
1449
1450   # In your application
1451   my $rs = $schema->resultset('Artist')->search(
1452     { 'track.title' => 'Teardrop' },
1453     {
1454       join     => { cd => 'track' },
1455       order_by => 'artist.name',
1456     }
1457   );
1458
1459 If the same join is supplied twice, it will be aliased to <rel>_2 (and
1460 similarly for a third time). For e.g.
1461
1462   my $rs = $schema->resultset('Artist')->search({
1463     'cds.title'   => 'Down to Earth',
1464     'cds_2.title' => 'Popular',
1465   }, {
1466     join => [ qw/cds cds/ ],
1467   });
1468
1469 will return a set of all artists that have both a cd with title 'Down
1470 to Earth' and a cd with title 'Popular'.
1471
1472 If you want to fetch related objects from other tables as well, see C<prefetch>
1473 below.
1474
1475 =head2 prefetch
1476
1477 =over 4
1478
1479 =item Value: ($rel_name | \@rel_names | \%rel_names)
1480
1481 =back
1482
1483 Contains one or more relationships that should be fetched along with the main
1484 query (when they are accessed afterwards they will have already been
1485 "prefetched").  This is useful for when you know you will need the related
1486 objects, because it saves at least one query:
1487
1488   my $rs = $schema->resultset('Tag')->search(
1489     undef,
1490     {
1491       prefetch => {
1492         cd => 'artist'
1493       }
1494     }
1495   );
1496
1497 The initial search results in SQL like the following:
1498
1499   SELECT tag.*, cd.*, artist.* FROM tag
1500   JOIN cd ON tag.cd = cd.cdid
1501   JOIN artist ON cd.artist = artist.artistid
1502
1503 L<DBIx::Class> has no need to go back to the database when we access the
1504 C<cd> or C<artist> relationships, which saves us two SQL statements in this
1505 case.
1506
1507 Simple prefetches will be joined automatically, so there is no need
1508 for a C<join> attribute in the above search. If you're prefetching to
1509 depth (e.g. { cd => { artist => 'label' } or similar), you'll need to
1510 specify the join as well.
1511
1512 C<prefetch> can be used with the following relationship types: C<belongs_to>,
1513 C<has_one> (or if you're using C<add_relationship>, any relationship declared
1514 with an accessor type of 'single' or 'filter').
1515
1516 =head2 from
1517
1518 =over 4
1519
1520 =item Value: \@from_clause
1521
1522 =back
1523
1524 The C<from> attribute gives you manual control over the C<FROM> clause of SQL
1525 statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
1526 clauses.
1527
1528 NOTE: Use this on your own risk.  This allows you to shoot off your foot!
1529 C<join> will usually do what you need and it is strongly recommended that you
1530 avoid using C<from> unless you cannot achieve the desired result using C<join>.
1531
1532 In simple terms, C<from> works as follows:
1533
1534     [
1535         { <alias> => <table>, -join_type => 'inner|left|right' }
1536         [] # nested JOIN (optional)
1537         { <table.column> => <foreign_table.foreign_key> }
1538     ]
1539
1540     JOIN
1541         <alias> <table>
1542         [JOIN ...]
1543     ON <table.column> = <foreign_table.foreign_key>
1544
1545 An easy way to follow the examples below is to remember the following:
1546
1547     Anything inside "[]" is a JOIN
1548     Anything inside "{}" is a condition for the enclosing JOIN
1549
1550 The following examples utilize a "person" table in a family tree application.
1551 In order to express parent->child relationships, this table is self-joined:
1552
1553     # Person->belongs_to('father' => 'Person');
1554     # Person->belongs_to('mother' => 'Person');
1555
1556 C<from> can be used to nest joins. Here we return all children with a father,
1557 then search against all mothers of those children:
1558
1559   $rs = $schema->resultset('Person')->search(
1560       undef,
1561       {
1562           alias => 'mother', # alias columns in accordance with "from"
1563           from => [
1564               { mother => 'person' },
1565               [
1566                   [
1567                       { child => 'person' },
1568                       [
1569                           { father => 'person' },
1570                           { 'father.person_id' => 'child.father_id' }
1571                       ]
1572                   ],
1573                   { 'mother.person_id' => 'child.mother_id' }
1574               ],
1575           ]
1576       },
1577   );
1578
1579   # Equivalent SQL:
1580   # SELECT mother.* FROM person mother
1581   # JOIN (
1582   #   person child
1583   #   JOIN person father
1584   #   ON ( father.person_id = child.father_id )
1585   # )
1586   # ON ( mother.person_id = child.mother_id )
1587
1588 The type of any join can be controlled manually. To search against only people
1589 with a father in the person table, we could explicitly use C<INNER JOIN>:
1590
1591     $rs = $schema->resultset('Person')->search(
1592         undef,
1593         {
1594             alias => 'child', # alias columns in accordance with "from"
1595             from => [
1596                 { child => 'person' },
1597                 [
1598                     { father => 'person', -join_type => 'inner' },
1599                     { 'father.id' => 'child.father_id' }
1600                 ],
1601             ]
1602         },
1603     );
1604
1605     # Equivalent SQL:
1606     # SELECT child.* FROM person child
1607     # INNER JOIN person father ON child.father_id = father.id
1608
1609 =head2 page
1610
1611 =over 4
1612
1613 =item Value: $page
1614
1615 =back
1616
1617 Makes the resultset paged and specifies the page to retrieve. Effectively
1618 identical to creating a non-pages resultset and then calling ->page($page)
1619 on it.
1620
1621 =head2 rows
1622
1623 =over 4
1624
1625 =item Value: $rows
1626
1627 =back
1628
1629 Specifes the maximum number of rows for direct retrieval or the number of
1630 rows per page if the page attribute or method is used.
1631
1632 =head2 group_by
1633
1634 =over 4
1635
1636 =item Value: \@columns
1637
1638 =back
1639
1640 A arrayref of columns to group by. Can include columns of joined tables.
1641
1642   group_by => [qw/ column1 column2 ... /]
1643
1644 =head2 having
1645
1646 =over 4
1647
1648 =item Value: $condition
1649
1650 =back
1651
1652 HAVING is a select statement attribute that is applied between GROUP BY and
1653 ORDER BY. It is applied to the after the grouping calculations have been
1654 done. 
1655
1656   having => { 'count(employee)' => { '>=', 100 } }
1657
1658 =head2 distinct
1659
1660 =over 4
1661
1662 =item Value: (0 | 1)
1663
1664 =back
1665
1666 Set to 1 to group by all columns.
1667
1668 =head2 cache
1669
1670 Set to 1 to cache search results. This prevents extra SQL queries if you
1671 revisit rows in your ResultSet:
1672
1673   my $resultset = $schema->resultset('Artist')->search( undef, { cache => 1 } );
1674   
1675   while( my $artist = $resultset->next ) {
1676     ... do stuff ...
1677   }
1678
1679   $rs->first; # without cache, this would issue a query
1680
1681 By default, searches are not cached.
1682
1683 For more examples of using these attributes, see
1684 L<DBIx::Class::Manual::Cookbook>.
1685
1686 =cut
1687
1688 1;