From 1e6c8c4dfb236ea8155668c8332c9d1321d929c1 Mon Sep 17 00:00:00 2001 From: Graham Barr Date: Thu, 12 Aug 2010 08:46:41 -0500 Subject: [PATCH] Add support for many to many mappings --- lib/MooseX/DBIC/Scaffold.pm | 79 +++++++++++++++++++----- lib/MooseX/DBIC/Scaffold/Mapping.pm | 14 +++++ lib/MooseX/DBIC/Scaffold/Relationship.pm | 22 +++++++ 3 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 lib/MooseX/DBIC/Scaffold/Mapping.pm diff --git a/lib/MooseX/DBIC/Scaffold.pm b/lib/MooseX/DBIC/Scaffold.pm index bec5175..782cb00 100644 --- a/lib/MooseX/DBIC/Scaffold.pm +++ b/lib/MooseX/DBIC/Scaffold.pm @@ -9,6 +9,7 @@ use Lingua::EN::Inflect::Number qw(to_S to_PL); use Data::Dumper; use MooseX::DBIC::Scaffold::Component; use MooseX::DBIC::Scaffold::Relationship; +use MooseX::DBIC::Scaffold::Mapping; has 'schema' => ( @@ -200,6 +201,15 @@ sub _ignore_constraint { return $ignore; } +sub mapping_accessor { + my ($self, $mapping) = @_; + my $method = + ($mapping->left->type eq 'has_many' or $mapping->right->type eq 'has_many') + ? 'to_plural' + : 'to_singular'; + $self->$method(lc $mapping->right->foreign_table->name); +} + sub relationship_accessor { my ($self, $relationship) = @_; my $method = $relationship->type . "_accessor"; @@ -265,13 +275,15 @@ sub column_accessor { my ($self, $column) = @_; return lc($column->name) } sub reciprocate_relationship { my ($self, $r) = @_; - MooseX::DBIC::Scaffold::Relationship->new( - name => $r->foreign_table->name . "__" . $r->name, - table => $r->foreign_table, - columns => [$r->foreign_columns], - foreign_table => $r->table, - foreign_columns => [$r->columns], - ); + $r->reciprocal; +} + +sub is_mapping_table { + my ($self, $t) = @_; + my $rel = $t->get_extra('relationships') or return 0; + ## FIXME, need better checks to determine a mapping table + return 0 unless @$rel == 2; + return 1; } sub produce { @@ -313,6 +325,34 @@ sub produce { } } + foreach my $t ($schema->get_tables) { + if ($self->is_mapping_table($t)) { + my $rel = $t->get_extra('relationships'); + next unless $rel and @$rel == 2; + my ($left, $right) = @$rel; + for my $loop (1, 2) { + my $lr = $left->reciprocal; + my $m = $self->build_mapping($lr, $right); + my $map = $lr->table->get_extra('mappings'); + $lr->table->add_extra(mappings => $map = []) unless $map; + push @$map, $m; + + ($left, $right) = ($right, $left); + } + } + } + + # Build accessor names + foreach my $t ($schema->get_tables) { + my $rel = $t->get_extra('relationships') or next; + $_->accessor($self->relationship_accessor($_)) for @$rel; + } + + foreach my $t ($schema->get_tables) { + my $map = $t->get_extra('mappings') or next; + $_->accessor($self->mapping_accessor($_)) for @$map; + } + $self->write(output => $fh); } @@ -332,6 +372,17 @@ sub build_relationship { ); } +sub build_mapping { + my ($self, $left, $right) = @_; + + my $r = MooseX::DBIC::Scaffold::Mapping->new( + name => $left->name . "__" . $right->name, + left => $left, + right => $right, + ); + return $r; +} + sub write { my ($self, %opt) = @_; @@ -431,7 +482,6 @@ sub write_table { } my @rel = @{$table->get_extra('relationships') || []}; - $_->accessor($self->relationship_accessor($_)) for @rel; foreach my $rel (sort { $a->accessor cmp $b->accessor } @rel) { my $foreign_class = $self->result_class($rel->foreign_table); my $type = $rel->type; @@ -444,14 +494,11 @@ sub write_table { print $fh $column_map, $attr, ");\n"; } -## FIXME -## foreach my $mapping ($table->mappings) { -## my $accessor = $mapping->accessor; -## my $step1 = $mapping->step1->accessor; -## my $step2 = $mapping->step2->accessor; -## my $attr = $mapping->attr ? ", " . _dump_data($mapping->attr," ") : ""; -## print $fh " __PACKAGE__->many_to_many( $accessor => qw[ $step1 $step2 ]$attr );\n"; -## } + my @map = @{$table->get_extra('mappings') || []}; + foreach my $map (sort { $a->accessor cmp $b->accessor } @map) { + printf $fh " __PACKAGE__->many_to_many( '%s' => qw[ %s %s ]);\n", + $map->accessor, $map->left->accessor, $map->right->accessor; + } if (keys %insert_default) { print $fh " sub insert {\n"; diff --git a/lib/MooseX/DBIC/Scaffold/Mapping.pm b/lib/MooseX/DBIC/Scaffold/Mapping.pm new file mode 100644 index 0000000..d4b15fd --- /dev/null +++ b/lib/MooseX/DBIC/Scaffold/Mapping.pm @@ -0,0 +1,14 @@ +## Copyright (C) Graham Barr +## vim: ts=8:sw=2:expandtab:shiftround + +package MooseX::DBIC::Scaffold::Mapping; + +use Moose; + +has name => (is => 'rw'); +has accessor => (is => 'rw'); +has left => (is => 'rw'); +has right => (is => 'rw'); + +1; + diff --git a/lib/MooseX/DBIC/Scaffold/Relationship.pm b/lib/MooseX/DBIC/Scaffold/Relationship.pm index 8c770b1..85dabc4 100644 --- a/lib/MooseX/DBIC/Scaffold/Relationship.pm +++ b/lib/MooseX/DBIC/Scaffold/Relationship.pm @@ -10,6 +10,7 @@ has type => (is => 'rw'); has accessor => (is => 'rw'); has table => (is => 'rw', weak_ref => 1); has foreign_table => (is => 'rw', weak_ref => 1); +has _reciprocal => (is => 'rw', weak_ref => 1); has columns => (is => 'rw', isa => 'ArrayRef', auto_deref => 1, default => sub { [] }); has foreign_columns => (is => 'rw', isa => 'ArrayRef', auto_deref => 1, default => sub { [] }); has attrs => ( @@ -24,6 +25,27 @@ has attrs => ( }, ); +sub _build_reciprocal { + my $self = shift; + + MooseX::DBIC::Scaffold::Relationship->new( + name => $self->foreign_table->name . "__" . $self->name, + table => $self->foreign_table, + columns => [$self->foreign_columns], + foreign_table => $self->table, + foreign_columns => [$self->columns], + _reciprocal => $self, + ); +} + +# We have to jump through hoops here because _reciprocal is a weakref, so we cannot use lazy_build +sub reciprocal { + my $self = shift; + my $r = $self->_reciprocal; + $self->_reciprocal($r = $self->_build_reciprocal) unless $r; + return $r; +} + sub BUILD { my ($self) = @_; return if $self->type;