From: Dagfinn Ilmari Mannsåker Date: Sat, 10 Nov 2012 23:01:59 +0000 (+0000) Subject: pass link table details to rel_name_map for many_to_many bridges (RT#81091) X-Git-Tag: 0.07034_01~3 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=da7342aeb23b35c714f73ebb732cb348045a374a;p=dbsrgits%2FDBIx-Class-Schema-Loader.git pass link table details to rel_name_map for many_to_many bridges (RT#81091) If two tables have multiple many-to-many link tables between them, the rel_name_map coderef needs to know which link table it's naming the rel for. --- diff --git a/Changes b/Changes index 704f03e..a37e44d 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,7 @@ Revision history for Perl extension DBIx::Class::Schema::Loader information_schema as information_schema does not work for readonly users - add rel_type param for relationship_attrs coderef + - pass link table details to rel_name_map for many_to_many bridges (RT#81091) 0.07033 2012-09-09 16:11:47 - more thoroughly document the new behavior for relationship attributes diff --git a/lib/DBIx/Class/Schema/Loader/Base.pm b/lib/DBIx/Class/Schema/Loader/Base.pm index 54ebe6b..2d3e3bb 100644 --- a/lib/DBIx/Class/Schema/Loader/Base.pm +++ b/lib/DBIx/Class/Schema/Loader/Base.pm @@ -618,6 +618,10 @@ If it is a coderef, the argument passed will be a hashref of this form: remote_class => name of the DBIC class we are related to, remote_moniker => moniker of the DBIC class we are related to, remote_columns => columns in the other table in the relationship, + # for type => "many_to_many" only: + link_class => name of the DBIC class for the link table + link_moniker => moniker of the DBIC class for the link table + link_rel_name => name of the relationship to the link table } DBICSL will try to use the value returned as the relationship name. diff --git a/lib/DBIx/Class/Schema/Loader/RelBuilder.pm b/lib/DBIx/Class/Schema/Loader/RelBuilder.pm index fac03d0..4134309 100644 --- a/lib/DBIx/Class/Schema/Loader/RelBuilder.pm +++ b/lib/DBIx/Class/Schema/Loader/RelBuilder.pm @@ -544,11 +544,13 @@ sub _generate_m2ms { my @class2_to_cols = apply { s/^foreign\.//i } keys %{ $rels->[0]{args}[2] }; + my $link_moniker = $rels->[0]{extra}{local_moniker}; + my @link_table_cols = - @{[ $self->schema->source($rels->[0]{extra}{local_moniker})->columns ]}; + @{[ $self->schema->source($link_moniker)->columns ]}; my @link_table_primary_cols = - @{[ $self->schema->source($rels->[0]{extra}{local_moniker})->primary_columns ]}; + @{[ $self->schema->source($link_moniker)->primary_columns ]}; next unless @class1_link_cols + @class2_link_cols == @link_table_cols && @link_table_cols == @link_table_primary_cols; @@ -562,6 +564,11 @@ sub _generate_m2ms { $class2, $class1_remote_moniker, \@class1_to_cols, + { + link_class => $class, + link_moniker => $link_moniker, + link_rel_name => $class1_to_link_table_rel_name, + }, ); $class1_to_class2_relname = $self->_resolve_relname_collision( @@ -579,6 +586,11 @@ sub _generate_m2ms { $class2, $class2_remote_moniker, \@class2_to_cols, + { + link_class => $class, + link_moniker => $link_moniker, + link_rel_name => $class2_to_link_table_rel_name, + }, ); $class2_to_class1_relname = $self->_resolve_relname_collision( @@ -909,9 +921,10 @@ sub _relnames_and_method { sub _rel_name_map { my ($self, $relname, $method, $local_class, $local_moniker, $local_cols, - $remote_class, $remote_moniker, $remote_cols) = @_; + $remote_class, $remote_moniker, $remote_cols, $extra) = @_; my $info = { + %{$extra || {}}, name => $relname, type => $method, local_class => $local_class, diff --git a/t/46relationships_multi_m2m.t b/t/46relationships_multi_m2m.t new file mode 100644 index 0000000..3f1773c --- /dev/null +++ b/t/46relationships_multi_m2m.t @@ -0,0 +1,68 @@ +use strict; +use warnings; +use Test::More; +use lib qw(t/lib); +use make_dbictest_db_multi_m2m; +use Devel::Dwarn; + +use DBIx::Class::Schema::Loader; + +my $schema_counter = 0; + +{ + my $hashmap = schema_with( + rel_name_map => { + foos_2s => "other_foos", + bars_2s => "other_bars", + }, + ); + + foreach ([qw(Foo bars)], [qw(Bar foos)]) { + my ($source, $rel) = @{$_}; + my $row = $hashmap->resultset($source)->find(1); + foreach my $link ("", "other_") { + can_ok $row, "${link}${rel}"; + } + } +} +{ + my $submap = schema_with( + rel_name_map => sub { + my ($args) = @_; + if ($args->{type} eq "many_to_many") { + like $args->{link_class}, + qr/\ADBICTest::Schema::${schema_counter}::Result::FooBar(?:One|Two)\z/, + "link_class"; + like $args->{link_moniker}, qr/\AFooBar(?:One|Two)\z/, + "link_moniker"; + like $args->{link_rel_name}, qr/\Afoo_bar_(?:ones|twoes)\z/, + "link_rel_name"; + + return $args->{name}."_".(split /_/, $args->{link_rel_name})[-1]; + } + }, + ); + foreach ([qw(Foo bars)], [qw(Bar foos)]) { + my ($source, $rel) = @{$_}; + my $row = $submap->resultset($source)->find(1); + foreach ([ones => 1], [twoes => 2]) { + my ($link, $count) = @{$_}; + my $m2m = "${rel}_${link}"; + can_ok $row, $m2m; + is $row->$m2m->count, $count, "$m2m count"; + } + } +} + +done_testing; + +#### generates a new schema with the given opts every time it's called +sub schema_with { + $schema_counter++; + DBIx::Class::Schema::Loader::make_schema_at( + 'DBICTest::Schema::'.$schema_counter, + { naming => 'current', @_ }, + [ $make_dbictest_db_multi_m2m::dsn ], + ); + "DBICTest::Schema::$schema_counter"->clone; +} diff --git a/t/lib/make_dbictest_db_multi_m2m.pm b/t/lib/make_dbictest_db_multi_m2m.pm new file mode 100644 index 0000000..6951876 --- /dev/null +++ b/t/lib/make_dbictest_db_multi_m2m.pm @@ -0,0 +1,43 @@ +package make_dbictest_db_multi_m2m; + +use strict; +use warnings; +use DBI; +use dbixcsl_test_dir qw/$tdir/; + +eval { require DBD::SQLite }; +my $class = $@ ? 'SQLite2' : 'SQLite'; + +my $fn = "$tdir/dbictest_multi_m2m.db"; + +unlink($fn); +our $dsn = "dbi:$class:dbname=$fn"; +my $dbh = DBI->connect($dsn); +$dbh->do('PRAGMA SYNCHRONOUS = OFF'); + +$dbh->do($_) for ( + q|CREATE TABLE foo ( + foo_id INTEGER PRIMARY KEY + )|, + q|CREATE TABLE bar ( + bar_id INTEGER PRIMARY KEY + )|, + q|CREATE TABLE foo_bar_one ( + foo_id INTEGER NOT NULL REFERENCES foo(foo_id), + bar_id INTEGER NOT NULL REFERENCES bar(bar_id), + PRIMARY KEY (foo_id, bar_id) + )|, + q|CREATE TABLE foo_bar_two ( + foo_id INTEGER NOT NULL REFERENCES foo(foo_id), + bar_id INTEGER NOT NULL REFERENCES bar(bar_id), + PRIMARY KEY (foo_id, bar_id) + )|, + q|INSERT INTO FOO (foo_id) VALUES (1), (2)|, + q|INSERT INTO BAR (bar_id) VALUES (1), (2)|, + q|INSERT INTO foo_bar_one (foo_id, bar_id) VALUES (1,1),(2,2)|, + q|INSERT INTO foo_bar_two (foo_id, bar_id) VALUES (1,1),(1,2),(2,1),(2,2)|, +); + +END { unlink($fn) unless $ENV{SCHEMA_LOADER_TESTS_NOCLEANUP}; } + +1;