Как я могу раскрыть отношения в стиле тегов «многие ко многим» в приложении Catalyst?

Я создаю приложение базы данных в Catalyst, используя jqGrid для выполнения грязной работы по обработке отображения данных. У меня почти все работает, кроме возможности фильтровать результаты поиска по "тегам". У меня есть три таблицы с такими отношениями:

package MyApp::Schema::Result::Project;
...
__PACKAGE__->has_many(
    "job_flags",
    "MyApp::Schema::Result::ProjectFlag",
    { "foreign.project_id" => "self.id" },
    { cascade_copy => 0, cascade_delete => 0 },
);
...
__PACKAGE__->many_to_many(flags => 'project_flags', 'flag');
1;

и

package MyApp::Schema::Result::Flag;
...
__PACKAGE__->has_many(
     "project_flags",
    "MyApp::Schema::Result::ProjectFlag",
    { "foreign.flag_id" => "self.id" },
    { cascade_copy => 0, cascade_delete => 0 },
);
...
__PACKAGE__->many_to_many(projects => 'project_flags', 'project');
1;

и, наконец, таблица соединений

package MyApp::Schema::Result::ProjectFlag;
...
__PACKAGE__->belongs_to(
    "flag",
    "MyApp::Schema::Result::Flag", 
    { id => "flag_id" },
    { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, 
);
...
__PACKAGE__->belongs_to(
    "project",
    "MyApp::Schema::Result::Project",
    { id => "project_id" },
    { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);
...
1;

В моем контроллере, который предоставляет данные JSON для jqGrid, я использую Catalyst::TraitFor::Controller::jQuery::jqGrid::Search для преобразования параметров запроса, сгенерированных jqGrid, в запросы в стиле DBIx::Class:

my $search_filter = $self->jqGrid_search($c->req->params);
my $project_rs = $c->model('DB::Project')->search(
    $search_filter, {
        join => 'project_flags',
        group_by => 'id',
    },
);

который затем передается генератору страниц jqGrid:

$project_rs = $self->jqgrid_page($c, $project_rs);

а затем я перебираю набор результатов и создаю свои столбцы jqGrid.

На стороне HTML я могу создать строку JSON, например

 {"groupOp":"AND","rules":[{"field":"project_flags.flag_id","op":"eq","data":"2"}]}

и, в этом случае, показать проекты, имеющие строку в project_flags с flag id из 2.

Я абсолютно точно знаю, что делаю это неправильно! Вся документация, которую я могу найти по Catalyst и DBIx::Class, демонстрирует похожие идеи, но я просто не могу понять, как применить их в этой ситуации. (не то чтобы я не пробовал).

  • Как мне создать средства доступа типа «has_flag($flag_id)», а затем использовать их из API jqGrid? Где в моем приложении Catalyst это место?
  • Один из способов, который я хотел бы отфильтровать, - это также отсутствие определенного флага.

person ruok    schedule 19.11.2013    source источник
comment
Вы определяете методы в классе набора результатов: metacpan.org/pod/distribution/DBIx-Class/lib/DBIx/Class/Manual/   -  person mikew    schedule 20.03.2014
comment
В этом вопросе нет ничего, что на самом деле имело бы какое-либо отношение к Catalyst.   -  person hobbs    schedule 17.07.2014


Ответы (1)


Должен быть честным с вами, я не совсем уверен, что понимаю ваш вопрос. Кажется, то, о чем вы спрашиваете, больше связано с DBIx::Class, чем с Catalyst — о последнем я знаю очень мало, о первом я узнаю все больше каждый день. Имея это в виду, вот моя лучшая попытка ответить на ваш вопрос. Я использую Mojolicious в качестве MVC, так как это то, что я знаю лучше всего.

Во-первых, я начинаю с создания базы данных «многие ко многим»:

CREATE TABLE project(
  id INTEGER PRIMARY KEY,
  name text
);

CREATE TABLE flag(
  id INTEGER PRIMARY KEY,
  name text
);

CREATE TABLE project_flag(
  project_id integer not null,
  flag_id integer not null,
  FOREIGN KEY(project_id) REFERENCES project(id),
  FOREIGN KEY(flag_id) REFERENCES flag(id)
);

INSERT INTO project (id,name) VALUES (1,'project1');
INSERT INTO project (id,name) VALUES (2,'project2');
INSERT INTO project (id,name) VALUES (3,'project3');

INSERT INTO flag (id,name) VALUES (1,'flag1');
INSERT INTO flag (id,name) VALUES (2,'flag2');
INSERT INTO flag (id,name) VALUES (3,'flag3');
INSERT INTO flag (id,name) VALUES (4,'flag4');

INSERT INTO project_flag (project_id,flag_id) VALUES (1,1);
INSERT INTO project_flag (project_id,flag_id) VALUES (1,2);
INSERT INTO project_flag (project_id,flag_id) VALUES (1,3);
INSERT INTO project_flag (project_id,flag_id) VALUES (1,4);
INSERT INTO project_flag (project_id,flag_id) VALUES (2,1);
INSERT INTO project_flag (project_id,flag_id) VALUES (2,4);

А вот мой код Perl (Mojolicious):

#!/usr/bin/env perl
use Mojolicious::Lite;
use Schema;

helper db => sub {
    return Schema->connect('dbi:SQLite:test.db');
};

get '/' => sub {
    my $self = shift;


    my $rs = $self->db->resultset('Project')->search(
        { 'me.name' => 'project3' },
        {
            join      =>  { 'project_flags' => 'flag' },
            select => ['me.name', 'flag.name'],
            as     => ['project', 'flag']
        }
    );

    $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');

    $self->render( json => [ $rs->all ] );
};

app->start;

И вот вывод JSON (красивая печать) из проекта1 (имеет связанные с ним флаги):

[
   {
      "project":"project1",
      "flag":"flag1"
   },
   {
      "flag":"flag2",
      "project":"project1"
   },
   {
      "project":"project1",
      "flag":"flag3"
   },
   {
      "flag":"flag4",
      "project":"project1"
   }
]

А вот JSON для проекта3 без привязки к каким-либо флагам:

[
   {
      "project":"project3",
      "flag":null
   }
]

Я разместил файлы на Github, чтобы вы могли проверить их. если хочешь.

В вашей ситуации, скажем, они ввели в фильтр слово «С++», и вы хотите вернуть все, что было помечено как «С++», тогда:

my $rs = $self->db->resultset('Tag')->search(
    { 'me.name' => 'c++' },
    {
        join      =>  { 'project_flags' => 'project' },
        select => ['me.name', 'project.name'],
        as     => ['tag', 'project']
    }
);

$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');

$self->render( json => [ $rs->all ] );

Если вы хотите вернуть все столбцы, используйте prefetch вместо соединения. Кроме того, если вы хотите поддерживать функцию автопоиска, измените search на search_like, например:

my $rs = $self->db->resultset('Tag')->search_like(
    { 'me.name' => $search_filter.'%' },

Я надеюсь, что если я не ответил на ваш вопрос, то то, что я дал, является по крайней мере толчком в правильном направлении.

person Franz Kafka    schedule 18.10.2014