【问题标题】:How to define and use many_to_many relationships in DBIx::Class?如何在 DBIx::Class 中定义和使用 many_to_many 关系?
【发布时间】:2019-03-06 18:28:13
【问题描述】:

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

book        book_language       language
=====  <->> ============== <<-> ========
bookID      book_languageID     languageID
title       bookID              language
            languageID

我用DBIx::Class::Schema::Loader生成了schema,对应的Result类在哪里:

Book
BookLanguage
Language

由于某些原因Loader 没有检测到这些表之间的 many_to_many 关系,所以我自己在Language 类中定义了这样的关系:

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

Book 类中:

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,在 Result 类中定义关系应该在这个类中创建一个访问器。我怎么能看到所有生成的访问器?如果我尝试使用Data::Printer 转储 ResultSet 对象,我只会看到列的访问器,但看不到关系的访问器。

如果我尝试列出关系:

say $schema->source('Book')->relationships; 

我在这里看不到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);

使用默认值生成架构:

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();

【问题讨论】:

  • DBIC::Schema::Loader 是否拾取了 book 和 book_language 以及 language 和 book_language 之间的多对一关系?他们叫什么?
  • @DaveCross 是的,装载机把它们捡起来了,它们都被命名为book_language_rel
  • 对于模式加载器来说,这是一个有点奇怪的名字。有关详细信息,请参阅下面的答案。
  • @DaveCross 我已经通过inflect_singularinflect_plural 选项更改了命名规则。但是现在在没有它们的情况下生成,并且在尝试访问 $book_rs-&gt;languages() 时仍然出现相同的错误

标签: perl orm dbix-class


【解决方案1】:

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

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

例如,调用 $rs-&gt;first-&gt;languages 即可。

如果您想要$book_rs 中所有书籍的所有语言,则必须在形成many_to_many 的两个关系上使用search_related。如果您预取这两个或在此之前不取回 rs,这将不会影响数据库,在这种情况下,将构建并执行优化的查询。

【讨论】:

    【解决方案2】:

    多对多关系建立在现有的“has_many”和“belongs_to”关系之上。如果它们设置不正确,many_to_many 关系也将不起作用。在您的情况下,我希望 DBIC::Schema::Loader 生成以下关系:

    在书.pm

    __PACKAGE__->has_many( 'book_languages'
                           'Your::DBIC::Schema::BookLanguages',
                           'bookID' );
    

    In Language.pm

    __PACKAGE__->has_many( 'book_languages'
                           'Your::DBIC::Schema::BookLanguages',
                           'languageID' );
    

    在 BookLanguage.pm 中

    __PACKAGE__->belongs_to( 'book'.,
                             'Your::DBIC::Schema::Book' );
    __PACKAGE__->belongs_to( 'language',
                             'Your::DBIC::Schema::Language' );
    

    然后你可以手动添加以下内容:

    在书.pm

    __PACKAGE->many_to_many( 'languages',
                             'book_languages',
                             'language' );
    

    In Language.pm

    __PACKAGE->many_to_many( 'books',
                             'book_languages',
                             'book' );
    

    【讨论】:

    • has_many 名称是 book_languages,但在 many_to_many 中,您指的是 book_language。错字?还是我错过了一些?顺便说一句,一个multi_to_multi 也被 Loader 拾取,但它给了我同样的错误/结果......
    • 对不起,是的,错字。现已修复。
    • 我添加了对整个测试用例的质疑。或许你可以试一试,仔细看看,哪里错了。
    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多