Merge 'bulk_create' into 'DBIx-Class-current'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Row.pm
index 24575f7..e4d885a 100644 (file)
@@ -44,19 +44,27 @@ passed objects.
 ## tests!
 
 sub new {
-  my ($class, $attrs, $source) = @_;
+  my ($class, $attrs) = @_;
   $class = ref $class if ref $class;
 
   my $new = { _column_data => {} };
   bless $new, $class;
 
-  $new->_source_handle($source) if $source;
+  if (my $handle = delete $attrs->{-source_handle}) {
+    $new->_source_handle($handle);
+  }
+  if (my $source = delete $attrs->{-result_source}) {
+    $new->result_source($source);
+  }
 
   if ($attrs) {
     $new->throw_exception("attrs must be a hashref")
       unless ref($attrs) eq 'HASH';
     
     my ($related,$inflated);
+    ## Pretend all the rels are actual objects, unset below if not, for insert() to fix
+    $new->{_rel_in_storage} = 1;
+
     foreach my $key (keys %$attrs) {
       if (ref $attrs->{$key}) {
         ## Can we extract this lot to use with update(_or .. ) ?
@@ -64,49 +72,48 @@ sub new {
         if ($info && $info->{attrs}{accessor}
           && $info->{attrs}{accessor} eq 'single')
         {
-          my $rel_obj = $attrs->{$key};
-          $new->{_rel_in_storage} = 1;
+          my $rel_obj = delete $attrs->{$key};
           if(!Scalar::Util::blessed($rel_obj)) {
-            $rel_obj = $new->new_related($key, $rel_obj);
-            $new->{_rel_in_storage} = 0;
+            $rel_obj = $new->find_or_new_related($key, $rel_obj);
+            $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
           }
-          $new->set_from_related($key, $attrs->{$key});        
-          $related->{$key} = $attrs->{$key};
+          $new->set_from_related($key, $rel_obj);        
+          $related->{$key} = $rel_obj;
           next;
         } elsif ($info && $info->{attrs}{accessor}
             && $info->{attrs}{accessor} eq 'multi'
             && ref $attrs->{$key} eq 'ARRAY') {
-            my $others = delete $attrs->{$key};
-            $new->{_rel_in_storage} = 1;
-            foreach my $rel_obj (@$others) {
-              if(!Scalar::Util::blessed($rel_obj)) {
-                $rel_obj = $new->new_related($key, $rel_obj);
-                $new->{_rel_in_storage} = 0;
-              }
+          my $others = delete $attrs->{$key};
+          foreach my $rel_obj (@$others) {
+            if(!Scalar::Util::blessed($rel_obj)) {
+              $rel_obj = $new->new_related($key, $rel_obj);
+              $new->{_rel_in_storage} = 0;
             }
-            $related->{$key} = $others;
-            next;
-        } elsif ($class->has_column($key)
-          && exists $class->column_info($key)->{_inflate_info})
+          }
+          $related->{$key} = $others;
+          next;
+        } elsif ($info && $info->{attrs}{accessor}
+          && $info->{attrs}{accessor} eq 'filter')
         {
           ## 'filter' should disappear and get merged in with 'single' above!
-          my $rel_obj = $attrs->{$key};
-          $new->{_rel_in_storage} = 1;
+          my $rel_obj = delete $attrs->{$key};
           if(!Scalar::Util::blessed($rel_obj)) {
-            $rel_obj = $new->new_related($key, $rel_obj);
-            $new->{_rel_in_storage} = 0;
+            $rel_obj = $new->find_or_new_related($key, $rel_obj);
+            $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
           }
           $inflated->{$key} = $rel_obj;
           next;
+        } elsif ($class->has_column($key)
+            && $class->column_info($key)->{_inflate_info}) {
+          $inflated->{$key} = $attrs->{$key};
+          next;
         }
       }
+      use Data::Dumper;
       $new->throw_exception("No such column $key on $class")
         unless $class->has_column($key);
       $new->store_column($key => $attrs->{$key});          
     }
-    if (my $source = delete $attrs->{-result_source}) {
-      $new->result_source($source);
-    }
 
     $new->{_relationship_data} = $related if $related;
     $new->{_inflated_column} = $inflated if $inflated;
@@ -135,20 +142,26 @@ sub insert {
     if $self->can('result_source_instance');
   $self->throw_exception("No result_source set on this object; can't insert")
     unless $source;
-
+  #use Data::Dumper; warn Dumper($self);
   # Check if we stored uninserted relobjs here in new()
   $source->storage->txn_begin if(!$self->{_rel_in_storage});
 
+  # Check if we stored uninserted relobjs here in new()
   my %related_stuff = (%{$self->{_relationship_data} || {}}, 
                        %{$self->{_inflated_column} || {}});
-  ## Should all be in relationship_data, but we need to get rid of the
-  ## 'filter' reltype..
-  ## These are the FK rels, need their IDs for the insert.
-  foreach my $relname (keys %related_stuff) {
-    my $relobj = $related_stuff{$relname};
-    if(ref $relobj ne 'ARRAY') {
-      $relobj->insert() if(!$relobj->in_storage);
-      $self->set_from_related($relname, $relobj);
+  if(!$self->{_rel_in_storage})
+  {
+    $source->storage->txn_begin;
+
+    ## Should all be in relationship_data, but we need to get rid of the
+    ## 'filter' reltype..
+    ## These are the FK rels, need their IDs for the insert.
+    foreach my $relname (keys %related_stuff) {
+      my $rel_obj = $related_stuff{$relname};
+      if(Scalar::Util::blessed($rel_obj) && $rel_obj->isa('DBIx::Class::Row')) {
+        $rel_obj->insert();
+        $self->set_from_related($relname, $rel_obj);
+      }
     }
   }
 
@@ -169,20 +182,23 @@ sub insert {
     $self->store_column($pri => $id);
   }
 
-  ## Now do the has_many rels, that need $selfs ID.
-  foreach my $relname (keys %related_stuff) {
-    my $relobj = $related_stuff{$relname};
-    if(ref $relobj eq 'ARRAY') {
-      foreach my $obj (@$relobj) {
-        my $info = $self->relationship_info($relname);
-        ## What about multi-col FKs ?
-        my $key = $1 if($info && (keys %{$info->{cond}})[0] =~ /^foreign\.(\w+)/);
-        $obj->set_from_related($key, $self);
-        $obj->insert() if(!$obj->in_storage);
+  if(!$self->{_rel_in_storage})
+  {
+    ## Now do the has_many rels, that need $selfs ID.
+    foreach my $relname (keys %related_stuff) {
+      my $relobj = $related_stuff{$relname};
+      if(ref $relobj eq 'ARRAY') {
+        foreach my $obj (@$relobj) {
+          my $info = $self->relationship_info($relname);
+          ## What about multi-col FKs ?
+          my $key = $1 if($info && (keys %{$info->{cond}})[0] =~ /^foreign\.(\w+)/);
+          $obj->set_from_related($key, $self);
+          $obj->insert() if(!$obj->in_storage);
+        }
       }
     }
+    $source->storage->txn_commit;
   }
-  $source->storage->txn_commit if(!$self->{_rel_in_storage});
 
   $self->in_storage(1);
   $self->{_dirty_columns} = {};
@@ -208,12 +224,16 @@ sub in_storage {
 
 =head2 update
 
-  $obj->update;
+  $obj->update \%columns?;
 
 Must be run on an object that is already in the database; issues an SQL
 UPDATE query to commit any changes to the object to the database if
 required.
 
+Also takes an options hashref of C<< column_name => value> pairs >> to update
+first. But be aware that this hashref might be edited in place, so dont rely on
+it being the same after a call to C<update>.
+
 =cut
 
 sub update {
@@ -385,6 +405,22 @@ sub get_dirty_columns {
            keys %{$self->{_dirty_columns}};
 }
 
+=head2 get_inflated_columns
+
+  my $inflated_data = $obj->get_inflated_columns;
+
+Similar to get_columns but objects are returned for inflated columns instead of their raw non-inflated values.
+
+=cut
+
+sub get_inflated_columns {
+  my $self = shift;
+  return map {
+    my $accessor = $self->column_info($_)->{'accessor'} || $_;
+    ($_ => $self->$accessor);
+  } $self->columns;
+}
+
 =head2 set_column
 
   $obj->set_column($col => $val);
@@ -525,6 +561,7 @@ sub inflate_result {
         $fetched = $pre_source->result_class->inflate_result(
                       $pre_source, @{$pre_val});
       }
+      $new->related_resultset($pre)->set_cache([ $fetched ]);
       my $accessor = $source->relationship_info($pre)->{attrs}{accessor};
       $class->throw_exception("No accessor for prefetched $pre")
        unless defined $accessor;