From: Stephan Szabo Date: Tue, 4 Apr 2006 22:25:11 +0000 (+0000) Subject: Added code to allow looking up of the other side of a relationship X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=de60a93dfda2e3384c2f6a00d55b352a4b4188c0;p=dbsrgits%2FDBIx-Class-Historic.git Added code to allow looking up of the other side of a relationship in order to get ON UPDATE/ON DELETE behavior for SQLT::Parser::DBIx::Class. --- diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 53da27b..3174957 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -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 diff --git a/lib/SQL/Translator/Parser/DBIx/Class.pm b/lib/SQL/Translator/Parser/DBIx/Class.pm index c0fece0..e2a3832 100644 --- a/lib/SQL/Translator/Parser/DBIx/Class.pm +++ b/lib/SQL/Translator/Parser/DBIx/Class.pm @@ -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; + diff --git a/t/helperrels/26sqlt.t b/t/helperrels/26sqlt.t index 66eea48..1362252 100644 --- a/t/helperrels/26sqlt.t +++ b/t/helperrels/26sqlt.t @@ -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 = (