метод доступа многие-ко-многим-ко-многим с использованием DBIx :: Class

Используя DBIx :: Class, я пытаюсь создать метод доступа «многие ко многим» (или даже просто отношение «много ко многим»), охватывающий две таблицы ссылок.

Речь идет о трех таблицах данных: Пользователи, Роли и Страницы, с двумя таблицами ссылок UserRoles и 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() методом. Итак, чтобы избежать этой ошибки, я решил попробовать создать метод доступа «многие ко многим» (или даже просто отношение «много ко многим») из Пользователи в Страницы. .

Можно ли создать такой аксессуар / отношение, которое охватывает две таблицы ссылок? Если да, то как? Я не могу найти пример в документации, и у меня самого кончились идеи. В противном случае, есть идеи о том, почему я получаю эту ошибку с помощью метода страниц ()?


person Waz    schedule 05.07.2012    source источник
comment
Метод поиска вернет один объект, а не ResultSet, если в RS есть только один элемент (или undef, если совпадение не найдено) - проблема исчезнет, ​​если вы измените его на - ›search_rs (...), заставляя его всегда возвращать ResultSet?   -  person RickF    schedule 06.07.2012
comment
Нет, к сожалению, я все еще получаю сообщение об ошибке Single parameters to new() must be a HASH ref. Возможно, лучше оставить его как search_rs, чтобы гарантировать, что он возвращает предсказуемый тип данных. Спасибо.   -  person Waz    schedule 06.07.2012
comment
Эта ошибка выглядит как ошибка Moose, а не как ошибка DBIx. Где-то вы вызываете new () с одним аргументом, отличным от hashref - вы уверены, что проблема в DBIx? Можете ли вы создать экземпляр $ user и вызвать $ user- ›pages () напрямую, чтобы узнать, возвращает ли он то, что вы думаете?   -  person RickF    schedule 06.07.2012
comment
Ты прав. Исключение возникает в /opt/ActivePerl-5.14/site/lib/Moose/Object.pm.   -  person Waz    schedule 07.07.2012
comment
Я поставил 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   -  person Waz    schedule 07.07.2012
comment
Я не уверен, что вы можете передать undef в качестве первого аргумента для поиска - вместо этого может потребоваться пустой hashref.   -  person RickF    schedule 09.07.2012
comment
->pages->search(undef,...), похоже, работает нормально, если у меня есть хороший pages() метод.   -  person Waz    schedule 12.07.2012


Ответы (1)


Насколько мне известно, встроенной поддержки отношений «многие-ко-многим-ко-многим» нет, но откатить собственную не так уж и сложно.

В схеме :: Результат :: 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.

person RickF    schedule 06.07.2012