From: Dagfinn Ilmari Mannsåker Date: Wed, 10 Oct 2007 16:48:14 +0000 (+0000) Subject: Generate one-to-one accessors for unique foreign keys X-Git-Tag: 0.04999_01~14 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=26f1c8c92bdbd5bf27e48005a1f3291ee7731a70;p=dbsrgits%2FDBIx-Class-Schema-Loader.git Generate one-to-one accessors for unique foreign keys --- diff --git a/lib/DBIx/Class/Schema/Loader/Base.pm b/lib/DBIx/Class/Schema/Loader/Base.pm index 77695c0..2d1b8fa 100644 --- a/lib/DBIx/Class/Schema/Loader/Base.pm +++ b/lib/DBIx/Class/Schema/Loader/Base.pm @@ -666,9 +666,10 @@ sub _load_relationships { $fkdef->{remote_source} = $self->monikers->{delete $fkdef->{remote_table}}; } + my $tbl_uniq_info = $self->_table_uniq_info($table); my $local_moniker = $self->monikers->{$table}; - my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info); + my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info, $tbl_uniq_info); foreach my $src_class (sort keys %$rel_stmts) { my $src_stmts = $rel_stmts->{$src_class}; diff --git a/lib/DBIx/Class/Schema/Loader/RelBuilder.pm b/lib/DBIx/Class/Schema/Loader/RelBuilder.pm index 196d09e..a292f2d 100644 --- a/lib/DBIx/Class/Schema/Loader/RelBuilder.pm +++ b/lib/DBIx/Class/Schema/Loader/RelBuilder.pm @@ -121,8 +121,19 @@ sub _inflect_singular { return Lingua::EN::Inflect::Number::to_S($relname); } +sub _array_eq { + my ($a, $b) = @_; + + return unless @$a == @$b; + + for (my $i = 0; $i < @$a; $i++) { + return unless $a->[$i] eq $b->[$i]; + } + return 1; +} + sub generate_code { - my ($self, $local_moniker, $rels) = @_; + my ($self, $local_moniker, $rels, $uniqs) = @_; my $all_code = {}; @@ -187,6 +198,16 @@ sub generate_code { delete $rev_cond{$_}; } + my $remote_method = 'has_many'; + + # If the local columns have a UNIQUE constraint, this is a one-to-one rel + my $primary = [ $self->{schema}->source($local_moniker)->primary_columns ]; + if (_array_eq($primary, $local_cols) || + grep { _array_eq($_->[1], $local_cols) } @$uniqs) { + $remote_method = 'might_have'; + $local_relname = $self->_inflect_singular($local_relname); + } + push(@{$all_code->{$local_class}}, { method => 'belongs_to', args => [ $remote_relname, @@ -197,7 +218,7 @@ sub generate_code { ); push(@{$all_code->{$remote_class}}, - { method => 'has_many', + { method => $remote_method, args => [ $local_relname, $local_class, \%rev_cond, diff --git a/t/lib/dbixcsl_common_tests.pm b/t/lib/dbixcsl_common_tests.pm index ac32964..2495b3b 100644 --- a/t/lib/dbixcsl_common_tests.pm +++ b/t/lib/dbixcsl_common_tests.pm @@ -43,7 +43,7 @@ sub _monikerize { sub run_tests { my $self = shift; - plan tests => 88; + plan tests => 97; $self->create(); @@ -310,6 +310,18 @@ sub run_tests { my $class26 = $classes->{loader_test26}; my $rsobj26 = $conn->resultset($moniker26); + my $moniker27 = $monikers->{loader_test27}; + my $class27 = $classes->{loader_test27}; + my $rsobj27 = $conn->resultset($moniker27); + + my $moniker28 = $monikers->{loader_test28}; + my $class28 = $classes->{loader_test28}; + my $rsobj28 = $conn->resultset($moniker28); + + my $moniker29 = $monikers->{loader_test29}; + my $class29 = $classes->{loader_test29}; + my $rsobj29 = $conn->resultset($moniker29); + isa_ok( $rsobj3, "DBIx::Class::ResultSet" ); isa_ok( $rsobj4, "DBIx::Class::ResultSet" ); isa_ok( $rsobj5, "DBIx::Class::ResultSet" ); @@ -326,6 +338,9 @@ sub run_tests { isa_ok( $rsobj22, "DBIx::Class::ResultSet" ); isa_ok( $rsobj25, "DBIx::Class::ResultSet" ); isa_ok( $rsobj26, "DBIx::Class::ResultSet" ); + isa_ok( $rsobj27, "DBIx::Class::ResultSet" ); + isa_ok( $rsobj28, "DBIx::Class::ResultSet" ); + isa_ok( $rsobj29, "DBIx::Class::ResultSet" ); # basic rel test my $obj4 = $rsobj4->find(123); @@ -384,6 +399,20 @@ sub run_tests { isa_ok($rs_rel26->first, $class26); is($rs_rel26->first->id, 3); + # test one-to-one rels + my $obj27 = $rsobj27->find(1); + my $obj28 = $obj27->loader_test28; + isa_ok($obj28, $class28); + is($obj28->get_column('id'), 1); + + my $obj29 = $obj27->loader_test29; + isa_ok($obj29, $class29); + is($obj29->id, 1); + + $obj27 = $rsobj27->find(2); + is($obj27->loader_test28, undef); + is($obj27->loader_test29, undef); + # from Chisel's tests... SKIP: { if($self->{vendor} =~ /sqlite/i) { @@ -758,6 +787,34 @@ sub create { q{ INSERT INTO loader_test26 (id,rel1,rel2) VALUES (33,5,7) }, q{ INSERT INTO loader_test26 (id,rel1,rel2) VALUES (3,42,42) }, + + qq{ + CREATE TABLE loader_test27 ( + id INTEGER NOT NULL PRIMARY KEY + ) $self->{innodb} + }, + + q{ INSERT INTO loader_test27 (id) VALUES (1) }, + q{ INSERT INTO loader_test27 (id) VALUES (2) }, + + qq{ + CREATE TABLE loader_test28 ( + id INTEGER NOT NULL PRIMARY KEY, + FOREIGN KEY (id) REFERENCES loader_test27 (id) + ) $self->{innodb} + }, + + q{ INSERT INTO loader_test28 (id) VALUES (1) }, + + qq{ + CREATE TABLE loader_test29 ( + id INTEGER NOT NULL PRIMARY KEY, + fk INTEGER UNIQUE, + FOREIGN KEY (fk) REFERENCES loader_test27 (id) + ) $self->{innodb} + }, + + q{ INSERT INTO loader_test29 (id,fk) VALUES (1,1) }, ); my @statements_advanced = ( @@ -884,6 +941,9 @@ sub drop_tables { loader_test21 loader_test26 loader_test25 + loader_test28 + loader_test29 + loader_test27 /; my @tables_advanced = qw/