fix a couple mistakes in last commit
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / ResultSource.pm
index ab4cf8e..e8b1b96 100644 (file)
@@ -7,6 +7,7 @@ use DBIx::Class::ResultSet;
 use Carp::Clan qw/^DBIx::Class/;
 
 use Storable;
+use Scalar::Util qw/weaken/;
 
 use base qw/DBIx::Class/;
 __PACKAGE__->load_components(qw/AccessorGroup/);
@@ -33,7 +34,7 @@ retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
 sub new {
   my ($class, $attrs) = @_;
   $class = ref $class if ref $class;
-  my $new = bless({ %{$attrs || {}} }, $class);
+  my $new = bless({ %{$attrs || {}}, _resultset => undef }, $class);
   $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
   $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} };
   $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
@@ -158,7 +159,7 @@ sub has_column {
   return exists $self->_columns->{$column};
 }
 
-=head2 column_info 
+=head2 column_info
 
   my $info = $obj->column_info($col);
 
@@ -206,7 +207,9 @@ sub columns {
   return @{$self->{_ordered_columns}||[]};
 }
 
-=head2 set_primary_key(@cols)
+=head2 set_primary_key
+
+=head3 Arguments: (@cols)
 
 Defines one or more columns as primary key for this source. Should be
 called after C<add_columns>.
@@ -388,7 +391,7 @@ sub add_relationship {
   1;
 }
 
-=head2 relationships()
+=head2 relationships
 
 Returns all valid relationship names for this source
 
@@ -398,7 +401,9 @@ sub relationships {
   return keys %{shift->_relationships};
 }
 
-=head2 relationship_info($relname)
+=head2 relationship_info
+
+=head3 Arguments: ($relname)
 
 Returns the relationship information for the specified relationship name
 
@@ -409,7 +414,9 @@ sub relationship_info {
   return $self->_relationships->{$rel};
 } 
 
-=head2 has_relationship($rel)
+=head2 has_relationship
+
+=head3 Arguments: ($rel)
 
 Returns 1 if the source has a relationship of this name, 0 otherwise.
 
@@ -420,7 +427,9 @@ sub has_relationship {
   return exists $self->_relationships->{$rel};
 }
 
-=head2 resolve_join($relation)
+=head2 resolve_join
+
+=head3 Arguments: ($relation)
 
 Returns the join structure required for the related result source
 
@@ -453,7 +462,9 @@ sub resolve_join {
   }
 }
 
-=head2 resolve_condition($cond, $as, $alias|$object)
+=head2 resolve_condition
+
+=head3 Arguments: ($cond, $as, $alias|$object)
 
 Resolves the passed condition to a concrete query fragment. If given an alias,
 returns a join condition; if given an object, inverts that object to produce
@@ -474,6 +485,8 @@ sub resolve_condition {
         #warn "$self $k $for $v";
         $ret{$k} = $for->get_column($v);
         #warn %ret;
+      } elsif (ref $as) { # reverse object
+        $ret{$v} = $as->get_column($k);
       } else {
         $ret{"${as}.${k}"} = "${for}.${v}";
       }
@@ -486,7 +499,9 @@ sub resolve_condition {
   }
 }
 
-=head2 resolve_prefetch (hashref/arrayref/scalar)
+=head2 resolve_prefetch
+
+=head3 Arguments: (hashref/arrayref/scalar)
 
 Accepts one or more relationships for the current source and returns an
 array of column names for each of those relationships. Column names are
@@ -530,41 +545,64 @@ in the supplied relationships. Examples:
 =cut
 
 sub resolve_prefetch {
-  my ($self, $pre, $alias, $seen) = @_;
+  my ($self, $pre, $alias, $seen, $order, $collapse) = @_;
   $seen ||= {};
-  use Data::Dumper;
   #$alias ||= $self->name;
   #warn $alias, Dumper $pre;
   if( ref $pre eq 'ARRAY' ) {
-    return map { $self->resolve_prefetch( $_, $alias, $seen ) } @$pre;
+    return
+      map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) }
+        @$pre;
   }
   elsif( ref $pre eq 'HASH' ) {
     my @ret =
     map {
-      $self->resolve_prefetch($_, $alias, $seen),
+      $self->resolve_prefetch($_, $alias, $seen, $order, $collapse),
       $self->related_source($_)->resolve_prefetch(
-                                   $pre->{$_}, "${alias}.$_", $seen)
-        } keys %$pre;
+               $pre->{$_}, "${alias}.$_", $seen, $order, $collapse)
+    } keys %$pre;
     #die Dumper \@ret;
     return @ret;
   }
   elsif( ref $pre ) {
-    $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre);
+    $self->throw_exception(
+      "don't know how to resolve prefetch reftype ".ref($pre));
   }
   else {
     my $count = ++$seen->{$pre};
     my $as = ($count > 1 ? "${pre}_${count}" : $pre);
     my $rel_info = $self->relationship_info( $pre );
-    $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info;
+    $self->throw_exception( $self->name . " has no such relationship '$pre'" )
+      unless $rel_info;
     my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : '');
+    my $rel_source = $self->related_source($pre);
+
+    if (exists $rel_info->{attrs}{accessor}
+         && $rel_info->{attrs}{accessor} eq 'multi') {
+      $self->throw_exception(
+        "Can't prefetch has_many ${pre} (join cond too complex)")
+        unless ref($rel_info->{cond}) eq 'HASH';
+      my @key = map { (/^foreign\.(.*)$/ ? ($1) : ()); }
+                    keys %{$rel_info->{cond}};
+      $collapse->{"${as_prefix}${pre}"} = \@key;
+      my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY'
+                   ? @{$rel_info->{attrs}{order_by}}
+                   : (defined $rel_info->{attrs}{order_by}
+                       ? ($rel_info->{attrs}{order_by})
+                       : ()));
+      push(@$order, map { "${as}.$_" } (@key, @ord));
+    }
+
     return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
-      $self->related_source($pre)->columns;
+      $rel_source->columns;
     #warn $alias, Dumper (\@ret);
     #return @ret;
   }
 }
 
-=head2 related_source($relname)
+=head2 related_source
+
+=head3 Arguments: ($relname)
 
 Returns the result source object for the given relationship
 
@@ -598,7 +636,13 @@ Specify here any attributes you wish to pass to your specialised resultset.
 
 sub resultset {
   my $self = shift;
-  return $self->resultset_class->new($self, $self->{resultset_attributes});
+  $self->throw_exception('resultset does not take any arguments. If you want another resultset, call it on the schema instead.') if scalar @_;
+  return $self->{_resultset} if ref $self->{_resultset} eq $self->resultset_class;
+  return $self->{_resultset} = do {
+    my $rs = $self->resultset_class->new($self, $self->{resultset_attributes});
+    weaken $rs->result_source;
+    $rs;
+  };
 }
 
 =head2 throw_exception