minor doc fix
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Row.pm
index e5fb4c9..84e6f93 100644 (file)
@@ -35,6 +35,8 @@ passed a hashref or an arrayref of hashrefs as the value, these will
 be turned into objects via new_related, and treated as if you had
 passed objects.
 
+For a more involved explanation, see L<DBIx::Class::ResultSet/create>.
+
 =cut
 
 ## It needs to store the new objects somewhere, and call insert on that list later when insert is called on this object. We may need an accessor for these so the user can retrieve them, if just doing ->new().
@@ -76,8 +78,10 @@ sub new {
           my $rel_obj = delete $attrs->{$key};
           if(!Scalar::Util::blessed($rel_obj)) {
             $rel_obj = $new->find_or_new_related($key, $rel_obj);
-            $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
           }
+
+          $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
+
           $new->set_from_related($key, $rel_obj);        
           $related->{$key} = $rel_obj;
           next;
@@ -90,6 +94,8 @@ sub new {
               $rel_obj = $new->new_related($key, $rel_obj);
               $new->{_rel_in_storage} = 0;
             }
+
+            $new->{_rel_in_storage} = 0 unless ($rel_obj->in_storage);
           }
           $related->{$key} = $others;
           next;
@@ -110,7 +116,6 @@ sub new {
           next;
         }
       }
-      use Data::Dumper;
       $new->throw_exception("No such column $key on $class")
         unless $class->has_column($key);
       $new->store_column($key => $attrs->{$key});          
@@ -133,6 +138,9 @@ be set, or the class to have a result_source_instance method. To insert
 an entirely new object into the database, use C<create> (see
 L<DBIx::Class::ResultSet/create>).
 
+This will also insert any uninserted, related objects held inside this
+one, see L<DBIx::Class::ResultSet/create> for more details.
+
 =cut
 
 sub insert {
@@ -150,9 +158,7 @@ sub insert {
   my %related_stuff = (%{$self->{_relationship_data} || {}}, 
                        %{$self->{_inflated_column} || {}});
 
-  if(!$self->{_rel_in_storage})
-  {
-
+  if(!$self->{_rel_in_storage}) {
     $source->storage->txn_begin;
 
     # The guard will save us if we blow out of this scope via die
@@ -166,24 +172,37 @@ sub insert {
     my @pri = $self->primary_columns;
 
     REL: foreach my $relname (keys %related_stuff) {
-      my $keyhash = $source->resolve_condition(
-                      $source->relationship_info($relname)->{cond},
-                      undef, 1
-                    ); # the above argset gives me the dependent cols on self
+
+      my $rel_obj = $related_stuff{$relname};
+
+      next REL unless (Scalar::Util::blessed($rel_obj)
+                       && $rel_obj->isa('DBIx::Class::Row'));
+
+      my $cond = $source->relationship_info($relname)->{cond};
+
+      next REL unless ref($cond) eq 'HASH';
+
+      # map { foreign.foo => 'self.bar' } to { bar => 'foo' }
+
+      my $keyhash = { map { my $x = $_; $x =~ s/.*\.//; $x; } reverse %$cond };
 
       # assume anything that references our PK probably is dependent on us
-      # rather than vice versa
+      # rather than vice versa, unless the far side is (a) defined or (b)
+      # auto-increment
 
       foreach my $p (@pri) {
-        next REL if exists $keyhash->{$p};
+        if (exists $keyhash->{$p}) {
+          unless (defined($rel_obj->get_column($keyhash->{$p}))
+                  || $rel_obj->column_info($keyhash->{$p})
+                             ->{is_auto_increment}) {
+            next REL;
+          }
+        }
       }
 
-      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);
-        delete $related_stuff{$relname};
-      }
+      $rel_obj->insert();
+      $self->set_from_related($relname, $rel_obj);
+      delete $related_stuff{$relname};
     }
   }
 
@@ -208,8 +227,7 @@ sub insert {
     $self->store_column($auto_pri[$_] => $ids[$_]) for 0 .. $#ids;
   }
 
-  if(!$self->{_rel_in_storage})
-  {
+  if(!$self->{_rel_in_storage}) {
     ## Now do the has_many rels, that need $selfs ID.
     foreach my $relname (keys %related_stuff) {
       my $rel_obj = $related_stuff{$relname};
@@ -224,7 +242,7 @@ sub insert {
         my $reverse = $source->reverse_relationship_info($relname);
         foreach my $obj (@cands) {
           $obj->set_from_related($_, $self) for keys %$reverse;
-          $obj->insert() if(!$obj->in_storage);
+          $obj->insert() unless ($obj->in_storage || $obj->result_source->resultset->search({$obj->get_columns})->count);
         }
       }
     }
@@ -244,7 +262,7 @@ sub insert {
   $obj->in_storage; # Get value
   $obj->in_storage(1); # Set value
 
-Indicated whether the object exists as a row in the database or not
+Indicates whether the object exists as a row in the database or not
 
 =cut
 
@@ -334,7 +352,7 @@ usable, but C<< ->in_storage() >> will now return 0 and the object must
 reinserted using C<< ->insert() >> before C<< ->update() >> can be used
 on it. If you delete an object in a class with a C<has_many>
 relationship, all the related objects will be deleted as well. To turn
-this behavior off, pass C<cascade_delete => 0> in the C<$attr>
+this behavior off, pass C<< cascade_delete => 0 >> in the C<$attr>
 hashref. Any database-level cascade or restrict will take precedence
 over a DBIx-Class-based cascading delete. See also L<DBIx::Class::ResultSet/delete>.
 
@@ -513,15 +531,29 @@ sub copy {
   $new->result_source($self->result_source);
   $new->set_columns($changes);
   $new->insert;
+
+  # Its possible we'll have 2 relations to the same Source. We need to make 
+  # sure we don't try to insert the same row twice esle we'll violate unique
+  # constraints
+  my $rels_copied = {};
+
   foreach my $rel ($self->result_source->relationships) {
     my $rel_info = $self->result_source->relationship_info($rel);
-    if ($rel_info->{attrs}{cascade_copy}) {
-      my $resolved = $self->result_source->resolve_condition(
-       $rel_info->{cond}, $rel, $new);
-      foreach my $related ($self->search_related($rel)) {
-        $related->copy($resolved);
-      }
+
+    next unless $rel_info->{attrs}{cascade_copy};
+  
+    my $resolved = $self->result_source->resolve_condition(
+      $rel_info->{cond}, $rel, $new
+    );
+
+    my $copied = $rels_copied->{ $rel_info->{source} } ||= {};
+    foreach my $related ($self->search_related($rel)) {
+      my $id_str = join("\0", $related->id);
+      next if $copied->{$id_str};
+      $copied->{$id_str} = 1;
+      my $rel_copy = $related->copy($resolved);
     }
   }
   return $new;
 }