Class::DBI(CDBI)で多対多(many_to_many)
なんかググっても全然良い例に出会えないの。誰も使ってないか、誰も嵌らないかどちらかだ。
半日つぶした。many_to_many が早くできて欲しいね。
テーブルはこんな感じだとする。(code がイけてねぇ〜。中間テーブルぐらい id 入れれば良かった…)
CREATE TABLE Users ( code varchar(8) primary key, passwd varchar(255) not null ); CREATE TABLE CourseUsers ( user_code varchar(8) not null, course_code varchar(255) not null, primary key (user_code, course_code) ); CREATE TABLE Courses ( code varchar(5) primary key, name varchar(255) not null );
lib/MyApp/Model/CDBI/Users.pm:
package MyApp::Model::CDBI::Users; use strict; __PACKAGE__->has_many(courses => ["MyApp::Model::CDBI::CourseUsers" => 'course_code'], 'user_code'); 1;
has_many ね。
第1引数のハッシュのキー(courses)は、取り出したいサブルーチン名。
第1引数のバリュー(["MyApp::〜", 〜])は、中間テーブルクラス名とその先を取得する為のキーを指定。
第2引数(user_code)は、自分のキーと結合する中間テーブルクラスの列名。
lib/MyApp/Model/CDBI/CourseUsers.pm:
package MyApp::Model::CDBI::CourseUsers; use strict; __PACKAGE__->has_a(course_code => "MyApp::Model::CDBI::Courses"); __PACKAGE__->has_a(user_code => "MyApp::Model::CDBI::Users"); sub courses { shift->user->courses } sub users { shift->course->users } 1;
has_a は簡単かな。自分の列名とそれに紐づくクラス名をハッシュで渡す。
(先の has_many の第1引数バリューのバリューと名前を合わせないといけないのだろうか?)
そして、サブルーチンがポイント! だけど何やってんのかワカラナス。shift で取り出してるのは $self だろうが、なんかバックして前に進んでるように見える、何故に? チョロQ?
こちらは、Users.pm と同じく:
lib/MyApp/Model/CDBI/Courses.pm:
package MyApp::Model::CDBI::Courses; use strict; __PACKAGE__->has_many(users => ["MyApp::Model::CDBI::CourseUsers" => 'user_code'], 'course_code'); 1;
アクセスはこんな感じ(もちろん lazyload):
lib/MyApp/Controller/適当.pm
my $user = MyApp::Model::CDBI::Users->retrieve("user-001"); foreach my $c ($user->courses) { $str .= $c->name . ", "; } die $str;
すごく謎だができてしまった。多分、説明が間違っているので Perl ハカーの解説希望。
プライマリキーが id だったら記述量も減って苦労しないと思います。カスケードして Insert や Delete したい時は add_trigger 辺りでホゲろ!
参考URL: