Merge 'trunk' into 'DBIx-Class-current'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
index e0e321a..0056abd 100644 (file)
@@ -32,6 +32,7 @@ In the examples below, the following table classes are used:
 
   package MyApp::Schema::Artist;
   use base qw/DBIx::Class/;
+  __PACKAGE__->load_components(qw/Core/);
   __PACKAGE__->table('artist');
   __PACKAGE__->add_columns(qw/artistid name/);
   __PACKAGE__->set_primary_key('artistid');
@@ -40,7 +41,8 @@ In the examples below, the following table classes are used:
 
   package MyApp::Schema::CD;
   use base qw/DBIx::Class/;
-  __PACKAGE__->table('artist');
+  __PACKAGE__->load_components(qw/Core/);
+  __PACKAGE__->table('cd');
   __PACKAGE__->add_columns(qw/cdid artist title year/);
   __PACKAGE__->set_primary_key('cdid');
   __PACKAGE__->belongs_to(artist => 'MyApp::Schema::Artist');
@@ -48,7 +50,9 @@ In the examples below, the following table classes are used:
 
 =head1 METHODS
 
-=head2 new($source, \%$attrs)
+=head2 new
+
+=head3 Arguments: ($source, \%$attrs)
 
 The resultset constructor. Takes a source object (usually a
 L<DBIx::Class::ResultSourceProxy::Table>) and an attribute hash (see L</ATRRIBUTES>
@@ -152,24 +156,30 @@ sub search {
   my $self = shift;
 
   #use Data::Dumper;warn Dumper(@_);
+  my $rs;
+  if( @_ ) {
+    
+    my $attrs = { %{$self->{attrs}} };
+    if (@_ > 1 && ref $_[$#_] eq 'HASH') {
+     $attrs = { %$attrs, %{ pop(@_) } };
+    }
 
-  my $attrs = { %{$self->{attrs}} };
-  if (@_ > 1 && ref $_[$#_] eq 'HASH') {
-    $attrs = { %$attrs, %{ pop(@_) } };
-  }
-
-  my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
-  if (defined $where) {
-    $where = (defined $attrs->{where}
+    my $where = (@_ ? ((@_ == 1 || ref $_[0] eq "HASH") ? shift : {@_}) : undef());
+    if (defined $where) {
+      $where = (defined $attrs->{where}
                 ? { '-and' =>
                     [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ }
                         $where, $attrs->{where} ] }
                 : $where);
-    $attrs->{where} = $where;
-  }
-
-  my $rs = (ref $self)->new($self->result_source, $attrs);
+      $attrs->{where} = $where;
+    }
 
+    $rs = (ref $self)->new($self->result_source, $attrs);
+  }
+  else {
+    $rs = $self;
+    $rs->reset();
+  }
   return (wantarray ? $rs->all : $rs);
 }
 
@@ -190,7 +200,9 @@ sub search_literal {
   return $self->search(\$cond, $attrs);
 }
 
-=head2 find(@colvalues), find(\%cols, \%attrs?)
+=head2 find
+
+=head3 Arguments: (@colvalues) | (\%cols, \%attrs?)
 
 Finds a row based on its primary key or unique constraint. For example:
 
@@ -322,7 +334,9 @@ sub search_like {
   return $class->search($query, { %$attrs });
 }
 
-=head2 slice($first, $last)
+=head2 slice
+
+=head3 Arguments: ($first, $last)
 
 Returns a subset of elements from the resultset.
 
@@ -370,6 +384,7 @@ sub _construct_object {
   my ($self, @row) = @_;
   my @row_orig = @row; # copy @row for key comparison later, because @row will change
   my @as = @{ $self->{attrs}{as} };
+#use Data::Dumper; warn Dumper \@as;
   #warn "@cols -> @row";
   my $info = [ {}, {} ];
   foreach my $as (@as) {
@@ -380,57 +395,9 @@ sub _construct_object {
     foreach my $p (@parts) {
       $target = $target->[1]->{$p} ||= [];
       
-      # if cache is enabled, fetch inflated objs for prefetch
-      if( $rs->{attrs}->{cache} ) {
-        my $rel_info = $rs->result_source->relationship_info($p);
-        my $cond = $rel_info->{cond};
-        my $parent_rs = $rs;
-        $rs = $rs->related_resultset($p);
-        $rs->{attrs}->{cache} = 1;
-        my @objs = ();
-          
-        # populate related resultset's cache if empty
-        if( !@{ $rs->get_cache } ) {
-          $rs->all;
-        }
-
-        # get ordinals for pk columns in $row, so values can be compared
-        my $map = {};
-        keys %$cond;
-        my $re = qr/^\w+\./;
-        while( my( $rel_key, $pk ) = ( each %$cond ) ) {
-          $rel_key =~ s/$re//;
-          $pk =~ s/$re//;
-          $map->{$rel_key} = $pk;
-        } #die Dumper $map;
-          
-        keys %$map;
-        while( my( $rel_key, $pk ) = each( %$map ) ) {
-          my $i = 0;
-          foreach my $col ( $parent_rs->result_source->columns ) {
-            if( $col eq $pk ) {
-              $map->{$rel_key} = $i;
-            }
-            $i++;
-          }
-        } #die Dumper $map;
-
-        $rs->reset(); # reset cursor/cache position 
-          
-        # get matching objects for inflation
-        OBJ: while( my $rel_obj = $rs->next ) {
-          keys %$map;
-          KEYS: while( my( $rel_key, $ordinal ) = each %$map ) {
-            # use get_column to avoid auto inflation (want scalar value)
-            if( $rel_obj->get_column($rel_key) ne $row_orig[$ordinal] ) {
-              next OBJ;
-            }
-            push @objs, $rel_obj;
-          }
-        }
-        $target->[0] = \@objs;
-      }
+      $rs = $rs->related_resultset($p) if $rs->{attrs}->{cache};
     }
+    
     $target->[0]->{$col} = shift @row
       if ref($target->[0]) ne 'ARRAY'; # arrayref is pre-inflated objects, do not overwrite
   }
@@ -439,10 +406,57 @@ sub _construct_object {
               $self->result_source, @$info);
   $new = $self->{attrs}{record_filter}->($new)
     if exists $self->{attrs}{record_filter};
+  if( $self->{attrs}->{cache} ) {
+    while( my( $rel, $rs ) = each( %{$self->{related_resultsets}} ) ) {
+      $rs->all;
+      #warn "$rel:", @{$rs->get_cache};
+    }
+    $self->build_rr( $self, $new );
+  }
   return $new;
 }
+  
+sub build_rr {
+  # build related resultsets for supplied object
+  my ( $self, $context, $obj ) = @_;
+  
+  my $re = qr/^\w+\./;
+  while( my ($rel, $rs) = each( %{$context->{related_resultsets}} ) ) {  
+    #warn "context:", $context->result_source->name, ", rel:$rel, rs:", $rs->result_source->name;
+    my @objs = ();
+    my $map = {};
+    my $cond = $context->result_source->relationship_info($rel)->{cond};
+    keys %$cond;
+    while( my( $rel_key, $pk ) = each(%$cond) ) {
+      $rel_key =~ s/$re//;
+      $pk =~ s/$re//;
+      $map->{$rel_key} = $pk;
+    }
+    
+    $rs->reset();
+    while( my $rel_obj = $rs->next ) {
+      while( my( $rel_key, $pk ) = each(%$map) ) {
+        if( $rel_obj->get_column($rel_key) eq $obj->get_column($pk) ) {
+          push @objs, $rel_obj;
+        }
+      }
+    }
 
-=head2 result_source 
+    my $rel_rs = $obj->related_resultset($rel);
+    $rel_rs->{attrs}->{cache} = 1;
+    $rel_rs->set_cache( \@objs );
+    
+    while( my $rel_obj = $rel_rs->next ) {
+      $self->build_rr( $rs, $rel_obj );
+    }
+    
+  }
+  
+}
+
+=head2 result_source
 
 Returns a reference to the result source for this recordset.
 
@@ -560,7 +574,9 @@ sub first {
   return $_[0]->reset->next;
 }
 
-=head2 update(\%values)
+=head2 update
+
+=head3 Arguments: (\%values)
 
 Sets the specified columns in the resultset to the supplied values.
 
@@ -573,7 +589,9 @@ sub update {
            $self->result_source->from, $values, $self->{cond});
 }
 
-=head2 update_all(\%values)
+=head2 update_all
+
+=head3 Arguments: (\%values)
 
 Fetches all objects and updates them one at a time.  Note that C<update_all>
 will run cascade triggers while L</update> will not.
@@ -652,7 +670,9 @@ sub pager {
     $self->{count}, $attrs->{rows}, $self->{page});
 }
 
-=head2 page($page_num)
+=head2 page
+
+=head3 Arguments: ($page_num)
 
 Returns a new resultset for the specified page.
 
@@ -665,7 +685,9 @@ sub page {
   return (ref $self)->new($self->result_source, $attrs);
 }
 
-=head2 new_result(\%vals)
+=head2 new_result
+
+=head3 Arguments: (\%vals)
 
 Creates a result in the resultset's result class.
 
@@ -687,7 +709,9 @@ sub new_result {
   $obj;
 }
 
-=head2 create(\%vals)
+=head2 create
+
+=head3 Arguments: (\%vals)
 
 Inserts a record into the resultset and returns the object.
 
@@ -701,7 +725,9 @@ sub create {
   return $self->new_result($attrs)->insert;
 }
 
-=head2 find_or_create(\%vals, \%attrs?)
+=head2 find_or_create
+
+=head3 Arguments: (\%vals, \%attrs?)
 
   $class->find_or_create({ key => $val, ... });
 
@@ -913,13 +939,17 @@ overview of them:
 Which column(s) to order the results by. This is currently passed through
 directly to SQL, so you can give e.g. C<foo DESC> for a descending order.
 
-=head2 cols (arrayref)
+=head2 cols
+
+=head3 Arguments: (arrayref)
 
 Shortcut to request a particular set of columns to be retrieved.  Adds
 C<me.> onto the start of any column without a C<.> in it and sets C<select>
 from that, then auto-populates C<as> from C<select> as normal.
 
-=head2 include_columns (arrayref)
+=head2 include_columns
+
+=head3 Arguments: (arrayref)
 
 Shortcut to include additional columns in the returned results - for example
 
@@ -927,7 +957,9 @@ Shortcut to include additional columns in the returned results - for example
 
 would add a 'name' column to the information passed to object inflation
 
-=head2 select (arrayref)
+=head2 select
+
+=head3 Arguments: (arrayref)
 
 Indicates which columns should be selected from the storage. You can use
 column names, or in the case of RDBMS back ends, function or stored procedure
@@ -948,7 +980,9 @@ When you use function/stored procedure names and do not supply an C<as>
 attribute, the column names returned are storage-dependent. E.g. MySQL would
 return a column named C<count(column_to_count)> in the above example.
 
-=head2 as (arrayref)
+=head2 as
+
+=head3 Arguments: (arrayref)
 
 Indicates column names for object inflation. This is used in conjunction with
 C<select>, usually when C<select> contains one or more function or stored
@@ -1026,7 +1060,9 @@ with title Bar.
 If you want to fetch related objects from other tables as well, see C<prefetch>
 below.
 
-=head2 prefetch arrayref/hashref
+=head2 prefetch
+
+=head3 Arguments: arrayref/hashref
 
 Contains one or more relationships that should be fetched along with the main 
 query (when they are accessed afterwards they will have already been
@@ -1061,7 +1097,9 @@ C<prefetch> can be used with the following relationship types: C<belongs_to>,
 C<has_one> (or if you're using C<add_relationship>, any relationship declared
 with an accessor type of 'single' or 'filter').
 
-=head2 from (arrayref)
+=head2 from
+
+=head3 Arguments: (arrayref)
 
 The C<from> attribute gives you manual control over the C<FROM> clause of SQL
 statements generated by L<DBIx::Class>, allowing you to express custom C<JOIN>
@@ -1161,7 +1199,9 @@ For a paged resultset, how many rows per page:
 
 Can also be used to simulate an SQL C<LIMIT>.
 
-=head2 group_by (arrayref)
+=head2 group_by
+
+=head3 Arguments: (arrayref)
 
 A arrayref of columns to group by. Can include columns of joined tables.