Added code to allow looking up of the other side of a relationship
Stephan Szabo [Tue, 4 Apr 2006 22:25:11 +0000 (22:25 +0000)]
in order to get ON UPDATE/ON DELETE behavior for
SQLT::Parser::DBIx::Class.

lib/DBIx/Class/ResultSource.pm
lib/SQL/Translator/Parser/DBIx/Class.pm
t/helperrels/26sqlt.t

index 53da27b..3174957 100644 (file)
@@ -485,6 +485,113 @@ sub has_relationship {
   return exists $self->_relationships->{$rel};
 }
 
+=head2 reverse_relationship_info
+
+=over 4
+
+=item Arguments: $relname
+
+=back
+
+Returns an array of hash references of relationship information for 
+the other side of the specified relationship name.
+
+=cut
+
+sub reverse_relationship_info {
+  my ($self, $rel) = @_;
+  my $rel_info = $self->relationship_info($rel);
+  my $ret = {};
+
+  return $ret unless ((ref $rel_info->{cond}) eq 'HASH');
+
+  my @cond = keys(%{$rel_info->{cond}});
+  my @refkeys = map {/^\w+\.(\w+)$/} @cond;
+  my @keys = map {$rel_info->{cond}->{$_} =~ /^\w+\.(\w+)$/} @cond;
+  
+  # Get the related result source for this relationship
+  my $othertable = $self->related_source($rel);
+
+  # Get all the relationships for that source that related to this source
+  # whose foreign column set are our self columns on $rel and whose self
+  # columns are our foreign columns on $rel.                
+  my @otherrels = $othertable->relationships();
+  my $otherrelationship;
+  foreach my $otherrel (@otherrels) {
+    my $otherrel_info = $othertable->relationship_info($otherrel);
+
+    my $back = $othertable->related_source($otherrel);
+    next unless $back->name eq $self->name;
+
+    my @othertestconds;
+
+    if (ref $otherrel_info->{cond} eq 'HASH') {
+      @othertestconds = ($otherrel_info->{cond});
+    }
+    elsif (ref $otherrel_info->{cond} eq 'ARRAY') {
+      @othertestconds = @{$otherrel_info->{cond}};
+    }
+    else {
+      next;
+    }
+
+    foreach my $othercond (@othertestconds) {
+      my @other_cond = keys(%$othercond);
+      my @other_refkeys = map {/^\w+\.(\w+)$/} @other_cond;
+      my @other_keys = map {$othercond->{$_} =~ /^\w+\.(\w+)$/} @other_cond;
+      next if (!$self->compare_relationship_keys(\@refkeys, \@other_keys) || 
+               !$self->compare_relationship_keys(\@other_refkeys, \@keys));
+      $ret->{$otherrel} =  $otherrel_info;
+    }
+  }
+  return $ret;
+}
+
+=head2 compare_relationship_keys
+
+=over 4
+
+=item Arguments: $keys1, $keys2
+
+=back
+
+Returns true if both sets of keynames are the same, false otherwise.
+
+=cut
+
+sub compare_relationship_keys {
+  my ($self, $keys1, $keys2) = @_;
+
+  # Make sure every keys1 is in keys2
+  my $found;
+  foreach my $key (@$keys1) {
+    $found = 0;
+    foreach my $prim (@$keys2) {
+      if ($prim eq $key) {
+        $found = 1;
+        last;
+      }
+    }
+    last unless $found;
+  }
+
+  # Make sure every key2 is in key1
+  if ($found) {
+    foreach my $prim (@$keys2) {
+      $found = 0;
+      foreach my $key (@$keys1) {
+        if ($prim eq $key) {
+          $found = 1;
+          last;
+        }
+      }
+      last unless $found;
+    }
+  }
+
+  return $found;
+}
+
 =head2 resolve_join
 
 =over 4
index c0fece0..e2a3832 100644 (file)
@@ -76,7 +76,7 @@ sub parse {
         my @primary = $source->primary_columns;
         my %unique_constraints = $source->unique_constraints;
         foreach my $uniq (keys %unique_constraints) {
-            if (!equal_keys($unique_constraints{$uniq}, \@primary)) {
+            if (!$source->compare_relationship_keys($unique_constraints{$uniq}, \@primary)) {
                 $table->add_constraint(
                             type             => 'unique',
                             name             => "$uniq",
@@ -90,11 +90,12 @@ sub parse {
         {
             my $rel_info = $source->relationship_info($rel);
 
-            my $rel_table = $source->related_source($rel)->name;
-
             # Ignore any rel cond that isn't a straight hash
             next unless ref $rel_info->{cond} eq 'HASH';
 
+            my $othertable = $source->related_source($rel);
+            my $rel_table = $othertable->name;
+
             # Get the key information, mapping off the foreign/self markers
             my @cond = keys(%{$rel_info->{cond}});
             my @refkeys = map {/^\w+\.(\w+)$/} @cond;
@@ -103,18 +104,31 @@ sub parse {
             if($rel_table)
             {
 
+                my $reverse_rels = $source->reverse_relationship_info($rel);
+                my ($otherrelname, $otherrelationship) = each %{$reverse_rels};
+
+                my $on_delete = '';
+                my $on_update = '';
+
+                if (defined $otherrelationship) {
+                    $on_delete = $otherrelationship->{'attrs'}->{cascade_delete} ? 'CASCADE' : '';
+                    $on_update = $otherrelationship->{'attrs'}->{cascade_copy} ? 'CASCADE' : '';
+                }
+
                 #Decide if this is a foreign key based on whether the self
                 #items are our primary columns.
 
                 # If the sets are different, then we assume it's a foreign key from
                 # us to another table.
-                if (!equal_keys(\@keys, \@primary)) {
+                if (!$source->compare_relationship_keys(\@keys, \@primary)) {
                     $table->add_constraint(
                                 type             => 'foreign_key',
                                 name             => "fk_$keys[0]",
                                 fields           => \@keys,
                                 reference_fields => \@refkeys,
                                 reference_table  => $rel_table,
+                                on_delete        => $on_delete,
+                                on_update        => $on_update
                     );
                 }
             }
@@ -123,42 +137,5 @@ sub parse {
     return 1;
 }
 
-# -------------------------------------------------------------------
-# equal_keys($key1, $key2)
-#
-# See if the set of keys in $key1 is equal to the set of keys in $key2
-# -------------------------------------------------------------------
-sub equal_keys {
-    my ($key1, $key2) = @_;
-
-    # Make sure every key1 is in key2
-    my $found;
-    foreach my $key (@$key1) {
-        $found = 0;
-        foreach my $prim (@$key2) {
-            if ($prim eq $key) {
-                $found = 1;
-                last;
-            }
-        }
-        last unless $found;
-    }
-
-    # Make sure every key2 is in key1
-    if ($found) {
-        foreach my $prim (@$key2) {
-            $found = 0;
-            foreach my $key (@$key1) {
-                if ($prim eq $key) {
-                    $found = 1;
-                    last;
-                }
-            }
-            last unless $found;
-        }
-    }
-
-    return $found;
-}
-
 1;
+
index 66eea48..1362252 100644 (file)
@@ -32,11 +32,11 @@ my @fk_constraints =
   {'display' => 'twokeys->artist',
    'selftable' => 'twokeys', 'foreigntable' => 'artist', 
    'selfcols'  => ['artist'], 'foreigncols' => ['artistid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => 'CASCADE'},
   {'display' => 'cd_to_producer->cd',
    'selftable' => 'cd_to_producer', 'foreigntable' => 'cd', 
    'selfcols'  => ['cd'], 'foreigncols' => ['cdid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => 'CASCADE'},
   {'display' => 'cd_to_producer->producer',
    'selftable' => 'cd_to_producer', 'foreigntable' => 'producer', 
    'selfcols'  => ['producer'], 'foreigncols' => ['producerid'],
@@ -44,7 +44,7 @@ my @fk_constraints =
   {'display' => 'self_ref_alias -> self_ref for self_ref',
    'selftable' => 'self_ref_alias', 'foreigntable' => 'self_ref', 
    'selfcols'  => ['self_ref'], 'foreigncols' => ['id'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => 'CASCADE'},
   {'display' => 'self_ref_alias -> self_ref for alias',
    'selftable' => 'self_ref_alias', 'foreigntable' => 'self_ref', 
    'selfcols'  => ['alias'], 'foreigncols' => ['id'],
@@ -52,19 +52,19 @@ my @fk_constraints =
   {'display' => 'cd -> artist',
    'selftable' => 'cd', 'foreigntable' => 'artist', 
    'selfcols'  => ['artist'], 'foreigncols' => ['artistid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => 'CASCADE'},
   {'display' => 'artist_undirected_map -> artist for id1',
    'selftable' => 'artist_undirected_map', 'foreigntable' => 'artist', 
    'selfcols'  => ['id1'], 'foreigncols' => ['artistid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => ''},
   {'display' => 'artist_undirected_map -> artist for id2',
    'selftable' => 'artist_undirected_map', 'foreigntable' => 'artist', 
    'selfcols'  => ['id2'], 'foreigncols' => ['artistid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => ''},
   {'display' => 'track->cd',
    'selftable' => 'track', 'foreigntable' => 'cd', 
    'selfcols'  => ['cd'], 'foreigncols' => ['cdid'],
-   'needed' => 2, on_delete => '', on_update => ''},
+   'needed' => 2, on_delete => 'CASCADE', on_update => 'CASCADE'},
   {'display' => 'treelike -> treelike for parent',
    'selftable' => 'treelike', 'foreigntable' => 'treelike', 
    'selfcols'  => ['parent'], 'foreigncols' => ['id'],
@@ -76,7 +76,7 @@ my @fk_constraints =
   {'display' => 'tags -> cd',
    'selftable' => 'tags', 'foreigntable' => 'cd', 
    'selfcols'  => ['cd'], 'foreigncols' => ['cdid'],
-   'needed' => 1, on_delete => '', on_update => ''},
+   'needed' => 1, on_delete => 'CASCADE', on_update => 'CASCADE'},
  );
 
 my @unique_constraints = (