【问题标题】:many-to-many-to-many accessor using DBIx::Class使用 DBIx::Class 的多对多访问器
【发布时间】:2012-07-06 03:59:29
【问题描述】:

使用 DBIx::Class,我试图创建一个跨越两个链接表的多对多访问器(甚至只是一个多对多关系)。

有问题的三个数据表是UsersRolesPages,以及两个链接表UserRoles角色页面。这些表是相关的:

  • 一个用户有很多UserRoles
  • 一个角色有很多UserRoles
  • 一个Role有很多RolePages
  • 一个页面有很多RolePages

Catalyst 帮助脚本为我创建了这些关系和访问器:

package MyApp::Schema::Result::User;

__PACKAGE__->has_many(
  "user_roles",
  "MyApp::Schema::Result::UserRole",
  { "foreign.username" => "self.username" },
  { cascade_copy => 0, cascade_delete => 0 },
);

__PACKAGE__->many_to_many("roles", "user_roles", "role");


package MyApp::Schema::Result::Role;

__PACKAGE__->has_many(
  "role_pages",
  "MyApp::Schema::Result::RolePage",
  { "foreign.role" => "self.role" },
  { cascade_copy => 0, cascade_delete => 0 },
);

__PACKAGE__->has_many(
  "user_roles",
  "MyApp::Schema::Result::UserRole",
  { "foreign.role" => "self.role" },
  { cascade_copy => 0, cascade_delete => 0 },
);

__PACKAGE__->many_to_many("page_names", "role_pages", "page_name")

__PACKAGE__->many_to_many("usernames", "user_roles", "username");


package MyApp::Schema::Result::Page;

__PACKAGE__->has_many(
  "role_pages",
  "MyApp::Schema::Result::RolePage",
  { "foreign.page_name" => "self.page_name" },
  { cascade_copy => 0, cascade_delete => 0 },
);

__PACKAGE__->many_to_many("roles", "role_pages", "role");


package MyApp::Schema::Result::UserRole;

__PACKAGE__->belongs_to(
  "role",
  "MyApp::Schema::Result::Role",
  { role => "role" },
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);

__PACKAGE__->belongs_to(
  "username",
  "MyApp::Schema::Result::User",
  { username => "username" },
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);


package MyApp::Schema::Result::RolePage;

__PACKAGE__->belongs_to(
  "page_name",
  "MyApp::Schema::Result::Page",
  { page_name => "page_name" },
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);

__PACKAGE__->belongs_to(
  "role",
  "MyApp::Schema::Result::Role",
  { role => "role" },
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);

我的最终目标是有一种简洁的方式来获取应该为当前用户显示的页面列表。即:

foreach my $page ($c->user->pages) {
    # do something with $page
}

我在 MyApp::Schema::Result::User 类中确实有这个方法:

sub pages {
    my ($self) = @_;

    return $self->result_source->schema->resultset('RolePage')->search(
        {
            'username.username' => $self->username,
        },
        {
            join => [
                {
                    role => {user_roles => 'username'},
                },
                'page_name',
            ],
        }
    );
}

在我添加一个类来扩展 MyApp::Schema::Result::RolePage 之前一直有效。该课程的开头是这样的:

package MyApp::Schema::ResultSet::RolePage;

use Moose;
use namespace::autoclean;
extends qw/MyApp::Schema::ResultSetX::DisplayTable/;

has '+name' => ( default => 'RolePage' );

我在整个应用程序中都使用了这种技术来创建各种结果集,但现在只是遇到了麻烦。我收到此错误:

在 Lifeway::Controller::Root->auto 中捕获的异常 "new() 的单个参数必须是 HASH ref

这是由我的MyApp::Schema::Result::User->pages() 方法引起的。所以,为了避免这个错误,我想我会尝试创建一个从 UsersPages 的多对多访问器(甚至只是一个多对多关系) .

是否可以创建这样一个跨越两个链接表的访问器/关系?如果是这样,怎么做?我在文档中找不到示例,并且我自己的想法已经用完了。如果做不到这一点,关于为什么我的 pages() 方法会出现该错误的任何想法?

【问题讨论】:

  • 当 RS 中只有一个项目时,搜索方法将返回单个对象而不是 ResultSet(如果未找到匹配项,则返回 undef) - 如果将其更改为 - 问题会消失吗? >search_rs(...),强制它总是返回一个 ResultSet?
  • 不,不幸的是我仍然收到Single parameters to new() must be a HASH ref 错误。不过,最好将其保留为search_rs,以确保它返回可预测的数据类型。谢谢。
  • 该错误看起来像是 Moose 错误,而不是 DBIx 错误。您在某个地方使用单个非 hashref 参数调用 new() - 您确定问题出在 DBIx 中吗?你可以创建一个 $user 实例并直接调用 $user->pages() 来查看它是否返回了你认为的内容吗?
  • 你是对的。在/opt/ActivePerl-5.14/site/lib/Moose/Object.pm 中引发了异常。
  • 我将die Dumper $c->user->pages; 放在我的根控制器的开头。它似乎为您的新 pages() 方法(如下所述)和我的旧方法返回了一些明智的结果。但是,我的旧方法在此语句中使用时会导致错误:$c->user->pages->search(undef, {columns => ['page_name'], distinct => 1, order_by => 'sort_order'})->get_column('page_name')->all

标签: perl many-to-many catalyst dbix-class


【解决方案1】:

据我所知,没有对多对多关系的内置支持,但推出自己的关系并不难。

在 Schema::Result::User.pm 中

sub pages {
  my $self = shift;
  return $self->search_related('user_roles')
    ->search_related('role')
    ->search_related('role_pages')
    ->search_related('page_name');
    # Alternatively, if you need to eliminate duplicates:
    # ->search_related('page_name', {}, {distinct => 1});
}

我认为,这非常接近标准 DBIx 多对多关系桥的功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    相关资源
    最近更新 更多