如何定义和使用 DBIx::Class 中的 many_to_many 关系?
How to define and use many_to_many relationships in DBIx::Class?
我在数据库中有 3 个表,简化为:
book book_language language
===== <->> ============== <<-> ========
bookID book_languageID languageID
title bookID language
languageID
使用 DBIx::Class::Schema::Loader
我生成了模式,相应的结果在哪里 classes:
Book
BookLanguage
Language
由于某些原因 Loader
没有检测到这些表之间的 many_to_many 关系,所以我在 Language
class 中自己定义了这样的关系:
package R::RMT::Result::Language;
...
__PACKAGE__->many_to_many('books' => 'book_language_rel', 'bookid_rel');
并在 Book
class:
package R::RMT::Result::Book;
...
__PACKAGE__->many_to_many('languages' => 'book_language_rel', 'languageid_rel');
现在我希望像这样访问所有相关语言:
my $dsn = "DBI:mysql:database=rkBook";
my $schema = R::RMT->connect( $dsn, 'user', 'pwd' );
my $book_rs = $schema->resultset('Book');
say $book_rs->languages();
但是我得到了错误:
Can't locate object method "languages" via package "DBIx::Class::ResultSet" at ...
我错了什么?我尝试将文档中的片段拼凑起来,但显然我弄错了。我从未见过 many_to_many
关系应该如何运作的完整示例。
AFAIU,在结果 class 中定义关系应该在此 class 中创建一个访问器。我怎样才能看到所有生成的访问器?如果我尝试使用 Data::Printer
转储 ResultSet 对象,我只会看到列的访问器,但看不到关系的访问器。
如果我尝试列出关系:
say $schema->source('Book')->relationships;
我在这里看不到 many_to_many
关系(也不是那些被 DBIx::Class::Schema::Loader
拾取的关系),只有 has_many
s 和 belongs_to
s。
编辑。添加了最简单的测试用例:
创建表并填充数据
CREATE TABLE `book` (
`bookID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_estonian_ci NOT NULL DEFAULT '',
PRIMARY KEY (`bookID`),
KEY `title` (`title`)
) ENGINE=InnoDB;
CREATE TABLE `language` (
`languageID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`language` varchar(255) COLLATE utf8_estonian_ci NOT NULL DEFAULT '',
PRIMARY KEY (`languageID`),
KEY `language` (`language`)
) ENGINE=InnoDB;
CREATE TABLE `book_language` (
`book_languageID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`bookID` int(10) unsigned DEFAULT NULL,
`languageID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`book_languageID`),
UNIQUE KEY `book_language` (`bookID`,`languageID`),
CONSTRAINT `book_language_ibfk_1` FOREIGN KEY (`languageID`) REFERENCES `language` (`languageID`) ON DELETE SET NULL,
CONSTRAINT `book_language_ibfk_2` FOREIGN KEY (`bookID`) REFERENCES `book` (`bookID`) ON DELETE SET NULL
) ENGINE=InnoDB;
INSERT INTO language (language) VALUES ('estonian'), ('english'), ('polish');
INSERT INTO book (title) VALUES ('Eesti rahva ennemuistsed jutud'), ('Estonska-polska slovar'), ('21 facts about...'), ('Englis-Polish Dictionary');
INSERT INTO book_language (bookID, languageID) VALUES (1,1), (2,1), (2,3),(3,1),(3,2),(3,3),(4,2),(4,3);
使用默认值生成架构:
dbicdump -o dump_directory=./lib -o debug=1 My::Schema 'dbi:mysql:dbname=testbook' user password
在 Book.pm
中添加了 many_to_many
-定义
# Created by DBIx::Class::Schema::Loader v0.07046 @ 2017-03-21 18:49:05
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ipamXRkSe+HLXGdTGwzQ9w
__PACKAGE__->many_to_many('languages' => 'book_languages', 'languageid');
并且在Language.pm
# Created by DBIx::Class::Schema::Loader v0.07046 @ 2017-03-21 18:49:05
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nZyaWdriRpgEWDAcO3+CFw
__PACKAGE__->many_to_many('books' => 'book_languages', 'bookid');
运行 这个脚本:
#!/usr/bin/env perl
use strict; use warnings; use 5.014; use utf8::all;
use My::Schema;
my $dsn = "DBI:mysql:database=testbook";
my $schema = My::Schema->connect( $dsn, 'user', 'password' );
my $book_rs = $schema->resultset('Book');
say $book_rs->languages();
多对多关系建立在现有的 "has_many" 和 "belongs_to" 关系之上。如果设置不正确,many_to_many 关系也不会起作用。在您的情况下,我希望 DBIC::Schema::Loader 生成以下关系:
在Book.pm
__PACKAGE__->has_many( 'book_languages'
'Your::DBIC::Schema::BookLanguages',
'bookID' );
在Language.pm
__PACKAGE__->has_many( 'book_languages'
'Your::DBIC::Schema::BookLanguages',
'languageID' );
书中Language.pm
__PACKAGE__->belongs_to( 'book'.,
'Your::DBIC::Schema::Book' );
__PACKAGE__->belongs_to( 'language',
'Your::DBIC::Schema::Language' );
然后你可以手动添加以下内容:
在Book.pm
__PACKAGE->many_to_many( 'languages',
'book_languages',
'language' );
在Language.pm
__PACKAGE->many_to_many( 'books',
'book_languages',
'book' );
首先请注意,many_to_many
仅生成辅助方法(访问器),但没有 'real' 关系,因为您不能在连接和预取参数中使用它们。
您的示例代码的问题是您尝试在结果集对象上调用结果方法(生成的 'languages')。
例如调用 $rs->first->languages
即可。
如果您想要 $book_rs
中所有书籍的所有语言,则必须在形成 many_to_many
的两个关系上使用 search_related
。如果您预取两者或之前不获取 rs,这将不会访问数据库,在这种情况下,将构建并执行优化查询。
我在数据库中有 3 个表,简化为:
book book_language language
===== <->> ============== <<-> ========
bookID book_languageID languageID
title bookID language
languageID
使用 DBIx::Class::Schema::Loader
我生成了模式,相应的结果在哪里 classes:
Book
BookLanguage
Language
由于某些原因 Loader
没有检测到这些表之间的 many_to_many 关系,所以我在 Language
class 中自己定义了这样的关系:
package R::RMT::Result::Language;
...
__PACKAGE__->many_to_many('books' => 'book_language_rel', 'bookid_rel');
并在 Book
class:
package R::RMT::Result::Book;
...
__PACKAGE__->many_to_many('languages' => 'book_language_rel', 'languageid_rel');
现在我希望像这样访问所有相关语言:
my $dsn = "DBI:mysql:database=rkBook";
my $schema = R::RMT->connect( $dsn, 'user', 'pwd' );
my $book_rs = $schema->resultset('Book');
say $book_rs->languages();
但是我得到了错误:
Can't locate object method "languages" via package "DBIx::Class::ResultSet" at ...
我错了什么?我尝试将文档中的片段拼凑起来,但显然我弄错了。我从未见过 many_to_many
关系应该如何运作的完整示例。
AFAIU,在结果 class 中定义关系应该在此 class 中创建一个访问器。我怎样才能看到所有生成的访问器?如果我尝试使用 Data::Printer
转储 ResultSet 对象,我只会看到列的访问器,但看不到关系的访问器。
如果我尝试列出关系:
say $schema->source('Book')->relationships;
我在这里看不到 many_to_many
关系(也不是那些被 DBIx::Class::Schema::Loader
拾取的关系),只有 has_many
s 和 belongs_to
s。
编辑。添加了最简单的测试用例:
创建表并填充数据
CREATE TABLE `book` (
`bookID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_estonian_ci NOT NULL DEFAULT '',
PRIMARY KEY (`bookID`),
KEY `title` (`title`)
) ENGINE=InnoDB;
CREATE TABLE `language` (
`languageID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`language` varchar(255) COLLATE utf8_estonian_ci NOT NULL DEFAULT '',
PRIMARY KEY (`languageID`),
KEY `language` (`language`)
) ENGINE=InnoDB;
CREATE TABLE `book_language` (
`book_languageID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`bookID` int(10) unsigned DEFAULT NULL,
`languageID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`book_languageID`),
UNIQUE KEY `book_language` (`bookID`,`languageID`),
CONSTRAINT `book_language_ibfk_1` FOREIGN KEY (`languageID`) REFERENCES `language` (`languageID`) ON DELETE SET NULL,
CONSTRAINT `book_language_ibfk_2` FOREIGN KEY (`bookID`) REFERENCES `book` (`bookID`) ON DELETE SET NULL
) ENGINE=InnoDB;
INSERT INTO language (language) VALUES ('estonian'), ('english'), ('polish');
INSERT INTO book (title) VALUES ('Eesti rahva ennemuistsed jutud'), ('Estonska-polska slovar'), ('21 facts about...'), ('Englis-Polish Dictionary');
INSERT INTO book_language (bookID, languageID) VALUES (1,1), (2,1), (2,3),(3,1),(3,2),(3,3),(4,2),(4,3);
使用默认值生成架构:
dbicdump -o dump_directory=./lib -o debug=1 My::Schema 'dbi:mysql:dbname=testbook' user password
在 Book.pm
many_to_many
-定义
# Created by DBIx::Class::Schema::Loader v0.07046 @ 2017-03-21 18:49:05
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ipamXRkSe+HLXGdTGwzQ9w
__PACKAGE__->many_to_many('languages' => 'book_languages', 'languageid');
并且在Language.pm
# Created by DBIx::Class::Schema::Loader v0.07046 @ 2017-03-21 18:49:05
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:nZyaWdriRpgEWDAcO3+CFw
__PACKAGE__->many_to_many('books' => 'book_languages', 'bookid');
运行 这个脚本:
#!/usr/bin/env perl
use strict; use warnings; use 5.014; use utf8::all;
use My::Schema;
my $dsn = "DBI:mysql:database=testbook";
my $schema = My::Schema->connect( $dsn, 'user', 'password' );
my $book_rs = $schema->resultset('Book');
say $book_rs->languages();
多对多关系建立在现有的 "has_many" 和 "belongs_to" 关系之上。如果设置不正确,many_to_many 关系也不会起作用。在您的情况下,我希望 DBIC::Schema::Loader 生成以下关系:
在Book.pm
__PACKAGE__->has_many( 'book_languages'
'Your::DBIC::Schema::BookLanguages',
'bookID' );
在Language.pm
__PACKAGE__->has_many( 'book_languages'
'Your::DBIC::Schema::BookLanguages',
'languageID' );
书中Language.pm
__PACKAGE__->belongs_to( 'book'.,
'Your::DBIC::Schema::Book' );
__PACKAGE__->belongs_to( 'language',
'Your::DBIC::Schema::Language' );
然后你可以手动添加以下内容:
在Book.pm
__PACKAGE->many_to_many( 'languages',
'book_languages',
'language' );
在Language.pm
__PACKAGE->many_to_many( 'books',
'book_languages',
'book' );
首先请注意,many_to_many
仅生成辅助方法(访问器),但没有 'real' 关系,因为您不能在连接和预取参数中使用它们。
您的示例代码的问题是您尝试在结果集对象上调用结果方法(生成的 'languages')。
例如调用 $rs->first->languages
即可。
如果您想要 $book_rs
中所有书籍的所有语言,则必须在形成 many_to_many
的两个关系上使用 search_related
。如果您预取两者或之前不获取 rs,这将不会访问数据库,在这种情况下,将构建并执行优化查询。