如何在DBIx :: Class中定义和使用many_to_many关系?

w.k*_*w.k 5 perl orm dbix-class

我在数据库中有3个表,简化为:

book        book_language       language
=====  <->> ============== <<-> ========
bookID      book_languageID     languageID
title       bookID              language
            languageID
Run Code Online (Sandbox Code Playgroud)

使用DBIx::Class::Schema::Loader我生成的架构,对应的Result类在哪里:

Book
BookLanguage
Language
Run Code Online (Sandbox Code Playgroud)

由于某些原因Loader,未在这些表之间检测到many_to_many关系,因此我在此类中定义了自己的关系Language

package R::RMT::Result::Language;
...
__PACKAGE__->many_to_many('books' => 'book_language_rel', 'bookid_rel');
Run Code Online (Sandbox Code Playgroud)

Book课堂上:

package R::RMT::Result::Book;
...
__PACKAGE__->many_to_many('languages' => 'book_language_rel', 'languageid_rel');
Run Code Online (Sandbox Code Playgroud)

现在,我希望以此访问所有相关语言:

my $dsn = "DBI:mysql:database=rkBook";
my $schema = R::RMT->connect( $dsn, 'user', 'pwd' );

my $book_rs = $schema->resultset('Book');
say $book_rs->languages();
Run Code Online (Sandbox Code Playgroud)

但是我得到了错误:

Can't locate object method "languages" via package "DBIx::Class::ResultSet" at ...
Run Code Online (Sandbox Code Playgroud)

我错了吗?我尝试将文档中的线索拼凑在一起,但显然我弄错了。我从未见过一个完整的many_to_many关系如何运作的例子。

AFAIU,在Result类中定义关系应成为该类的访问者。我如何看到所有派生的访问者?如果我尝试转储ResultSet对象,则Data::Printer只能看到列的访问器,而没有关系的访问器。

如果我尝试列出以下关系:

say $schema->source('Book')->relationships; 
Run Code Online (Sandbox Code Playgroud)

我在这里没有看到many_to_many关系(也没有看到由拾取的关系DBIx::Class::Schema::Loader),只有has_manys和belongs_tos。

编辑。添加了最简单的测试用例:

创建表并填充数据

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);
Run Code Online (Sandbox Code Playgroud)

生成具有默认值的架构:

dbicdump -o dump_directory=./lib -o debug=1 My::Schema 'dbi:mysql:dbname=testbook' user password
Run Code Online (Sandbox Code Playgroud)

many_to_many在-definitions中添加了Book.pm

# 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');
Run Code Online (Sandbox Code Playgroud)

而在 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');
Run Code Online (Sandbox Code Playgroud)

运行此脚本:

#!/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();
Run Code Online (Sandbox Code Playgroud)

Ale*_*ier 3

首先请注意,它many_to_many仅生成辅助方法(访问器),但不是“真正的”关系,因为您不能在连接和预取参数中使用它们。

您的示例代码的问题是您尝试在结果集对象上调用结果方法(生成的“语言”)。

$rs->first->languages例如打电话就可以了。

如果您想要所有书籍的所有语言,$book_rs您必须使用search_related形成many_to_many. 如果您预取这两个值或在构建并执行优化查询之前不获取 rs,则不会访问数据库。