disabled ->resultset caching
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSource.pm
1 package DBIx::Class::ResultSource;
2
3 use strict;
4 use warnings;
5
6 use DBIx::Class::ResultSet;
7 use Carp::Clan qw/^DBIx::Class/;
8 use Storable;
9
10 use base qw/DBIx::Class/;
11 __PACKAGE__->load_components(qw/AccessorGroup/);
12
13 __PACKAGE__->mk_group_accessors('simple' => qw/_ordered_columns
14   _columns _primaries _unique_constraints name resultset_attributes
15   schema from _relationships/);
16
17 __PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class
18   result_class/);
19
20 =head1 NAME
21
22 DBIx::Class::ResultSource - Result source object
23
24 =head1 SYNOPSIS
25
26 =head1 DESCRIPTION
27
28 A ResultSource is a component of a schema from which results can be directly
29 retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
30
31 =head1 METHODS
32
33 =cut
34
35 sub new {
36   my ($class, $attrs) = @_;
37   $class = ref $class if ref $class;
38   my $new = bless({ %{$attrs || {}}, _resultset => undef }, $class);
39   $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
40   $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} };
41   $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
42   $new->{_columns} = { %{$new->{_columns}||{}} };
43   $new->{_relationships} = { %{$new->{_relationships}||{}} };
44   $new->{name} ||= "!!NAME NOT SET!!";
45   $new->{_columns_info_loaded} ||= 0;
46   return $new;
47 }
48
49 =pod
50
51 =head2 add_columns
52
53   $table->add_columns(qw/col1 col2 col3/);
54
55   $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
56
57 Adds columns to the result source. If supplied key => hashref pairs, uses
58 the hashref as the column_info for that column. Repeated calls of this
59 method will add more columns, not replace them.
60
61 The contents of the column_info are not set in stone. The following
62 keys are currently recognised/used by DBIx::Class:
63
64 =over 4
65
66 =item accessor
67
68 Use this to set the name of the accessor for this column. If unset,
69 the name of the column will be used.
70
71 =item data_type
72
73 This contains the column type. It is automatically filled by the
74 L<SQL::Translator::Producer::DBIx::Class::File> producer, and the
75 L<DBIx::Class::Schema::Loader> module. If you do not enter a
76 data_type, DBIx::Class will attempt to retrieve it from the
77 database for you, using L<DBI>'s column_info method. The values of this
78 key are typically upper-cased.
79
80 Currently there is no standard set of values for the data_type. Use
81 whatever your database supports.
82
83 =item size
84
85 The length of your column, if it is a column type that can have a size
86 restriction. This is currently not used by DBIx::Class.
87
88 =item is_nullable
89
90 Set this to a true value for a columns that is allowed to contain
91 NULL values. This is currently not used by DBIx::Class.
92
93 =item is_auto_increment
94
95 Set this to a true value for a column whose value is somehow
96 automatically set. This is used to determine which columns to empty
97 when cloning objects using C<copy>.
98
99 =item is_foreign_key
100
101 Set this to a true value for a column that contains a key from a
102 foreign table. This is currently not used by DBIx::Class.
103
104 =item default_value
105
106 Set this to the default value which will be inserted into a column
107 by the database. Can contain either a value or a function. This is
108 currently not used by DBIx::Class.
109
110 =item sequence
111
112 Set this on a primary key column to the name of the sequence used to
113 generate a new key value. If not specified, L<DBIx::Class::PK::Auto>
114 will attempt to retrieve the name of the sequence from the database
115 automatically.
116
117 =back
118
119 =head2 add_column
120
121   $table->add_column('col' => \%info?);
122
123 Convenience alias to add_columns.
124
125 =cut
126
127 sub add_columns {
128   my ($self, @cols) = @_;
129   $self->_ordered_columns(\@cols) unless $self->_ordered_columns;
130   
131   my @added;
132   my $columns = $self->_columns;
133   while (my $col = shift @cols) {
134     # If next entry is { ... } use that for the column info, if not
135     # use an empty hashref
136     my $column_info = ref $cols[0] ? shift(@cols) : {};
137     push(@added, $col) unless exists $columns->{$col};
138     $columns->{$col} = $column_info;
139   }
140   push @{ $self->_ordered_columns }, @added;
141   return $self;
142 }
143
144 *add_column = \&add_columns;
145
146 =head2 has_column
147
148   if ($obj->has_column($col)) { ... }
149
150 Returns true if the source has a column of this name, false otherwise.
151
152 =cut
153
154 sub has_column {
155   my ($self, $column) = @_;
156   return exists $self->_columns->{$column};
157 }
158
159 =head2 column_info
160
161   my $info = $obj->column_info($col);
162
163 Returns the column metadata hashref for a column. See the description
164 of add_column for information on the contents of the hashref.
165
166 =cut
167
168 sub column_info {
169   my ($self, $column) = @_;
170   $self->throw_exception("No such column $column")
171     unless exists $self->_columns->{$column};
172   #warn $self->{_columns_info_loaded}, "\n";
173   if ( ! $self->_columns->{$column}{data_type}
174        and ! $self->{_columns_info_loaded}
175        and $self->schema and $self->storage )
176   {
177     $self->{_columns_info_loaded}++;
178     my $info;
179     # eval for the case of storage without table
180     eval { $info = $self->storage->columns_info_for($self->from) };
181     unless ($@) {
182       foreach my $col ( keys %{$self->_columns} ) {
183         foreach my $i ( keys %{$info->{$col}} ) {
184             $self->_columns->{$col}{$i} = $info->{$col}{$i};
185         }
186       }
187     }
188   }
189   return $self->_columns->{$column};
190 }
191
192 =head2 columns
193
194   my @column_names = $obj->columns;
195
196 Returns all column names in the order they were declared to add_columns.
197
198 =cut
199
200 sub columns {
201   my $self = shift;
202   $self->throw_exception(
203     "columns() is a read-only accessor, did you mean add_columns()?"
204   ) if (@_ > 1);
205   return @{$self->{_ordered_columns}||[]};
206 }
207
208 =head2 set_primary_key
209
210 =over 4
211
212 =item Arguments: @cols
213
214 =back
215
216 Defines one or more columns as primary key for this source. Should be
217 called after C<add_columns>.
218
219 Additionally, defines a unique constraint named C<primary>.
220
221 The primary key columns are used by L<DBIx::Class::PK::Auto> to
222 retrieve automatically created values from the database.
223
224 =cut
225
226 sub set_primary_key {
227   my ($self, @cols) = @_;
228   # check if primary key columns are valid columns
229   foreach my $col (@cols) {
230     $self->throw_exception("No such column $col on table " . $self->name)
231       unless $self->has_column($col);
232   }
233   $self->_primaries(\@cols);
234
235   $self->add_unique_constraint(primary => \@cols);
236 }
237
238 =head2 primary_columns
239
240 Read-only accessor which returns the list of primary keys.
241
242 =cut
243
244 sub primary_columns {
245   return @{shift->_primaries||[]};
246 }
247
248 =head2 add_unique_constraint
249
250 Declare a unique constraint on this source. Call once for each unique
251 constraint. Unique constraints are used when you call C<find> on a
252 L<DBIx::Class::ResultSet>. Only columns in the constraint are searched,
253 for example:
254
255   # For UNIQUE (column1, column2)
256   __PACKAGE__->add_unique_constraint(
257     constraint_name => [ qw/column1 column2/ ],
258   );
259
260 =cut
261
262 sub add_unique_constraint {
263   my ($self, $name, $cols) = @_;
264
265   foreach my $col (@$cols) {
266     $self->throw_exception("No such column $col on table " . $self->name)
267       unless $self->has_column($col);
268   }
269
270   my %unique_constraints = $self->unique_constraints;
271   $unique_constraints{$name} = $cols;
272   $self->_unique_constraints(\%unique_constraints);
273 }
274
275 =head2 unique_constraints
276
277 Read-only accessor which returns the list of unique constraints on this source.
278
279 =cut
280
281 sub unique_constraints {
282   return %{shift->_unique_constraints||{}};
283 }
284
285 =head2 from
286
287 Returns an expression of the source to be supplied to storage to specify
288 retrieval from this source. In the case of a database, the required FROM
289 clause contents.
290
291 =cut
292
293 =head2 storage
294
295 Returns the storage handle for the current schema.
296
297 See also: L<DBIx::Class::Storage>
298
299 =cut
300
301 sub storage { shift->schema->storage; }
302
303 =head2 add_relationship
304
305   $source->add_relationship('relname', 'related_source', $cond, $attrs);
306
307 The relationship name can be arbitrary, but must be unique for each
308 relationship attached to this result source. 'related_source' should
309 be the name with which the related result source was registered with
310 the current schema. For example:
311
312   $schema->source('Book')->add_relationship('reviews', 'Review', {
313     'foreign.book_id' => 'self.id',
314   });
315
316 The condition C<$cond> needs to be an L<SQL::Abstract>-style
317 representation of the join between the tables. For example, if you're
318 creating a rel from Author to Book,
319
320   { 'foreign.author_id' => 'self.id' }
321
322 will result in the JOIN clause
323
324   author me JOIN book foreign ON foreign.author_id = me.id
325
326 You can specify as many foreign => self mappings as necessary.
327
328 Valid attributes are as follows:
329
330 =over 4
331
332 =item join_type
333
334 Explicitly specifies the type of join to use in the relationship. Any
335 SQL join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in
336 the SQL command immediately before C<JOIN>.
337
338 =item proxy
339
340 An arrayref containing a list of accessors in the foreign class to proxy in
341 the main class. If, for example, you do the following:
342   
343   CD->might_have(liner_notes => 'LinerNotes', undef, {
344     proxy => [ qw/notes/ ],
345   });
346   
347 Then, assuming LinerNotes has an accessor named notes, you can do:
348
349   my $cd = CD->find(1);
350   # set notes -- LinerNotes object is created if it doesn't exist
351   $cd->notes('Notes go here');
352
353 =item accessor
354
355 Specifies the type of accessor that should be created for the
356 relationship. Valid values are C<single> (for when there is only a single
357 related object), C<multi> (when there can be many), and C<filter> (for
358 when there is a single related object, but you also want the relationship
359 accessor to double as a column accessor). For C<multi> accessors, an
360 add_to_* method is also created, which calls C<create_related> for the
361 relationship.
362
363 =back
364
365 =cut
366
367 sub add_relationship {
368   my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
369   $self->throw_exception("Can't create relationship without join condition")
370     unless $cond;
371   $attrs ||= {};
372
373   my %rels = %{ $self->_relationships };
374   $rels{$rel} = { class => $f_source_name,
375                   source => $f_source_name,
376                   cond  => $cond,
377                   attrs => $attrs };
378   $self->_relationships(\%rels);
379
380   return $self;
381
382   # XXX disabled. doesn't work properly currently. skip in tests.
383
384   my $f_source = $self->schema->source($f_source_name);
385   unless ($f_source) {
386     eval "require $f_source_name;";
387     if ($@) {
388       die $@ unless $@ =~ /Can't locate/;
389     }
390     $f_source = $f_source_name->result_source;
391     #my $s_class = ref($self->schema);
392     #$f_source_name =~ m/^${s_class}::(.*)$/;
393     #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
394     #$f_source = $self->schema->source($f_source_name);
395   }
396   return unless $f_source; # Can't test rel without f_source
397
398   eval { $self->resolve_join($rel, 'me') };
399
400   if ($@) { # If the resolve failed, back out and re-throw the error
401     delete $rels{$rel}; #
402     $self->_relationships(\%rels);
403     $self->throw_exception("Error creating relationship $rel: $@");
404   }
405   1;
406 }
407
408 =head2 relationships
409
410 Returns all relationship names for this source.
411
412 =cut
413
414 sub relationships {
415   return keys %{shift->_relationships};
416 }
417
418 =head2 relationship_info
419
420 =over 4
421
422 =item Arguments: $relname
423
424 =back
425
426 Returns a hash of relationship information for the specified relationship
427 name.
428
429 =cut
430
431 sub relationship_info {
432   my ($self, $rel) = @_;
433   return $self->_relationships->{$rel};
434 }
435
436 =head2 has_relationship
437
438 =over 4
439
440 =item Arguments: $rel
441
442 =back
443
444 Returns true if the source has a relationship of this name, false otherwise.
445
446 =cut
447
448 sub has_relationship {
449   my ($self, $rel) = @_;
450   return exists $self->_relationships->{$rel};
451 }
452
453 =head2 resolve_join
454
455 =over 4
456
457 =item Arguments: $relation
458
459 =back
460
461 Returns the join structure required for the related result source.
462
463 =cut
464
465 sub resolve_join {
466   my ($self, $join, $alias, $seen) = @_;
467   $seen ||= {};
468   if (ref $join eq 'ARRAY') {
469     return map { $self->resolve_join($_, $alias, $seen) } @$join;
470   } elsif (ref $join eq 'HASH') {
471     return
472       map {
473         my $as = ($seen->{$_} ? $_.'_'.($seen->{$_}+1) : $_);
474         ($self->resolve_join($_, $alias, $seen),
475           $self->related_source($_)->resolve_join($join->{$_}, $as, $seen));
476       } keys %$join;
477   } elsif (ref $join) {
478     $self->throw_exception("No idea how to resolve join reftype ".ref $join);
479   } else {
480     my $count = ++$seen->{$join};
481     #use Data::Dumper; warn Dumper($seen);
482     my $as = ($count > 1 ? "${join}_${count}" : $join);
483     my $rel_info = $self->relationship_info($join);
484     $self->throw_exception("No such relationship ${join}") unless $rel_info;
485     my $type = $rel_info->{attrs}{join_type} || '';
486     return [ { $as => $self->related_source($join)->from,
487                -join_type => $type },
488              $self->resolve_condition($rel_info->{cond}, $as, $alias) ];
489   }
490 }
491
492 =head2 resolve_condition
493
494 =over 4
495
496 =item Arguments: $cond, $as, $alias|$object
497
498 =back
499
500 Resolves the passed condition to a concrete query fragment. If given an alias,
501 returns a join condition; if given an object, inverts that object to produce
502 a related conditional from that object.
503
504 =cut
505
506 sub resolve_condition {
507   my ($self, $cond, $as, $for) = @_;
508   #warn %$cond;
509   if (ref $cond eq 'HASH') {
510     my %ret;
511     while (my ($k, $v) = each %{$cond}) {
512       # XXX should probably check these are valid columns
513       $k =~ s/^foreign\.// ||
514         $self->throw_exception("Invalid rel cond key ${k}");
515       $v =~ s/^self\.// ||
516         $self->throw_exception("Invalid rel cond val ${v}");
517       if (ref $for) { # Object
518         #warn "$self $k $for $v";
519         $ret{$k} = $for->get_column($v);
520         #warn %ret;
521       } elsif (ref $as) { # reverse object
522         $ret{$v} = $as->get_column($k);
523       } else {
524         $ret{"${as}.${k}"} = "${for}.${v}";
525       }
526     }
527     return \%ret;
528   } elsif (ref $cond eq 'ARRAY') {
529     return [ map { $self->resolve_condition($_, $as, $for) } @$cond ];
530   } else {
531    die("Can't handle this yet :(");
532   }
533 }
534
535 =head2 resolve_prefetch
536
537 =over 4
538
539 =item Arguments: hashref/arrayref/scalar
540
541 =back
542
543 Accepts one or more relationships for the current source and returns an
544 array of column names for each of those relationships. Column names are
545 prefixed relative to the current source, in accordance with where they appear
546 in the supplied relationships. Examples:
547
548   my $source = $schema->resultset('Tag')->source;
549   @columns = $source->resolve_prefetch( { cd => 'artist' } );
550
551   # @columns =
552   #(
553   #  'cd.cdid',
554   #  'cd.artist',
555   #  'cd.title',
556   #  'cd.year',
557   #  'cd.artist.artistid',
558   #  'cd.artist.name'
559   #)
560
561   @columns = $source->resolve_prefetch( qw[/ cd /] );
562
563   # @columns =
564   #(
565   #   'cd.cdid',
566   #   'cd.artist',
567   #   'cd.title',
568   #   'cd.year'
569   #)
570
571   $source = $schema->resultset('CD')->source;
572   @columns = $source->resolve_prefetch( qw[/ artist producer /] );
573
574   # @columns =
575   #(
576   #  'artist.artistid',
577   #  'artist.name',
578   #  'producer.producerid',
579   #  'producer.name'
580   #)
581
582 =cut
583
584 sub resolve_prefetch {
585   my ($self, $pre, $alias, $seen, $order, $collapse) = @_;
586   $seen ||= {};
587   #$alias ||= $self->name;
588   #warn $alias, Dumper $pre;
589   if( ref $pre eq 'ARRAY' ) {
590     return
591       map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) }
592         @$pre;
593   }
594   elsif( ref $pre eq 'HASH' ) {
595     my @ret =
596     map {
597       $self->resolve_prefetch($_, $alias, $seen, $order, $collapse),
598       $self->related_source($_)->resolve_prefetch(
599                $pre->{$_}, "${alias}.$_", $seen, $order, $collapse)
600     } keys %$pre;
601     #die Dumper \@ret;
602     return @ret;
603   }
604   elsif( ref $pre ) {
605     $self->throw_exception(
606       "don't know how to resolve prefetch reftype ".ref($pre));
607   }
608   else {
609     my $count = ++$seen->{$pre};
610     my $as = ($count > 1 ? "${pre}_${count}" : $pre);
611     my $rel_info = $self->relationship_info( $pre );
612     $self->throw_exception( $self->name . " has no such relationship '$pre'" )
613       unless $rel_info;
614     my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : '');
615     my $rel_source = $self->related_source($pre);
616
617     if (exists $rel_info->{attrs}{accessor}
618          && $rel_info->{attrs}{accessor} eq 'multi') {
619       $self->throw_exception(
620         "Can't prefetch has_many ${pre} (join cond too complex)")
621         unless ref($rel_info->{cond}) eq 'HASH';
622       my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); }
623                     keys %{$rel_info->{cond}};
624       $collapse->{"${as_prefix}${pre}"} = \@key;
625       my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY'
626                    ? @{$rel_info->{attrs}{order_by}}
627                    : (defined $rel_info->{attrs}{order_by}
628                        ? ($rel_info->{attrs}{order_by})
629                        : ()));
630       push(@$order, map { "${as}.$_" } (@key, @ord));
631     }
632
633     return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
634       $rel_source->columns;
635     #warn $alias, Dumper (\@ret);
636     #return @ret;
637   }
638 }
639
640 =head2 related_source
641
642 =over 4
643
644 =item Arguments: $relname
645
646 =back
647
648 Returns the result source object for the given relationship.
649
650 =cut
651
652 sub related_source {
653   my ($self, $rel) = @_;
654   if( !$self->has_relationship( $rel ) ) {
655     $self->throw_exception("No such relationship '$rel'");
656   }
657   return $self->schema->source($self->relationship_info($rel)->{source});
658 }
659
660 =head2 related_class
661
662 =over 4
663
664 =item Arguments: $relname
665
666 =back
667
668 Returns the class name for objects in the given relationship.
669
670 =cut
671
672 sub related_class {
673   my ($self, $rel) = @_;
674   if( !$self->has_relationship( $rel ) ) {
675     $self->throw_exception("No such relationship '$rel'");
676   }
677   return $self->schema->class($self->relationship_info($rel)->{source});
678 }
679
680 =head2 resultset
681
682 Returns a resultset for the given source. This will initially be created
683 on demand by calling
684
685   $self->resultset_class->new($self, $self->resultset_attributes)
686
687 but is cached from then on unless resultset_class changes.
688
689 =head2 resultset_class
690
691 Set the class of the resultset, this is useful if you want to create your
692 own resultset methods. Create your own class derived from
693 L<DBIx::Class::ResultSet>, and set it here.
694
695 =head2 resultset_attributes
696
697 Specify here any attributes you wish to pass to your specialised resultset.
698
699 =cut
700
701 sub resultset {
702   my $self = shift;
703   $self->throw_exception(
704     'resultset does not take any arguments. If you want another resultset, '.
705     'call it on the schema instead.'
706   ) if scalar @_;
707
708   # disabled until we can figure out a way to do it without consistency issues
709   #
710   #return $self->{_resultset}
711   #  if ref $self->{_resultset} eq $self->resultset_class;
712   #return $self->{_resultset} =
713
714   return $self->resultset_class->new(
715     $self, $self->{resultset_attributes}
716   );
717 }
718
719 =head2 throw_exception
720
721 See L<DBIx::Class::Schema/"throw_exception">.
722
723 =cut
724
725 sub throw_exception {
726   my $self = shift;
727   if (defined $self->schema) {
728     $self->schema->throw_exception(@_);
729   } else {
730     croak(@_);
731   }
732 }
733
734 =head1 AUTHORS
735
736 Matt S. Trout <mst@shadowcatsystems.co.uk>
737
738 =head1 LICENSE
739
740 You may distribute this code under the same terms as Perl itself.
741
742 =cut
743