[Dbix-class] ->copy for has_one relationships broken?

Mark Trostler mark at zzo.com
Thu May 22 19:53:00 BST 2008


Ok having more time to think about it - here's what is the problem (I 
think):

->copy works great for 'multi' type relationships.
However when putting cascade_copy => 1 on a 'has_one' relationship:

package PBDB::Ingredient;

...
     hardware_spec_id => {
                         data_type           => 'INT',
                         is_nullable         => 0,
                     },
...

__PACKAGE__->has_one(hardware_spec => 'PBDB::Hardware_Spec',
     { 'foreign.id' => 'self.hardware_spec_id' }, { cascade_copy => 1 });

-----

package PDBD::Hardware_Spec;

...
     id          => {
                         data_type           => 'INT',
                         is_nullable         => 0,
                         is_auto_increment   => 1,
                     },
...


__PACKAGE__->has_many(ingredients => 'PBDB::Ingredient',
     { 'foreign.hardware_spec_id' => 'self.id' }, { cascade_copy => 0 });

...


Strange Thing happen on ->copy.

First, the $resolved hash has the primary auto incremented key of the 
hardware_spec row 'id' - so when ->copy is called recursively on it the 
insert fails (as it's a dup entry).

Second, since the recursive relationship-creating copy call happens 
after the $new object is inserted, the $new object's has_one 
relationship with the cascaded copy never gets set to the newly created 
cascaded object.

So here's a patch that seems to fix both problem:

--- /home/y/lib/perl5/site_perl/5.8/DBIx/Class/Row.pm   2007-08-01 
12:02:11.000000000 -0700
+++ hack/Row.pm 2008-05-22 11:33:58.000000000 -0700
@@ -518,6 +518,11 @@ sub copy {
        if $self->result_source->column_info($col)->{is_auto_increment};
    }

+  foreach my $col (keys %$changes) {
+    delete $changes->{$col}
+      if $self->result_source->column_info($col)->{is_auto_increment};
+  }
+
    my $new = { _column_data => $col_data };
    bless $new, ref $self;

@@ -530,10 +535,14 @@ sub copy {
        my $resolved = $self->result_source->resolve_condition(
         $rel_info->{cond}, $rel, $new);
        foreach my $related ($self->search_related($rel)) {
-        $related->copy($resolved);
+        my $copy = $related->copy($resolved);
+        if ($rel_info->{attrs}->{accessor} eq 'single') {
+            $new->update_from_related($rel, $copy);
+        }
        }
      }
    }



I think that makes sense...  Of course I could be missing something very 
  obvious.
thanks,
	Mark





More information about the DBIx-Class mailing list