DBICのRの拡張
WEB+DBが発売されてます。
id:naoyaさんの記事はDBICです!
買うべしべし。
DBICでRを拡張する時の話ですが、WEB+DBにも載っているとおり、
CDBIとは異なりDBを操作する時にResultSetというレイヤを経由して
もりもりDBを触るのですが、その為、CDBIで簡単にできていたことができなくなっています。
それはRの拡張です。
例えば弊社の場合、idの代わりにridがユニークなキーになってるので、
ridでレコードを検索する場合があり、
CDBIの場合
retrieve_ridというメソッドをCDBIに生やして、
Proj::Data::Blog->retrieve_rid('et5qZkSihU');
こんな感じでやってました、これをDBICで同じことを実現しようとした場合、
結構厄介だったりします。
例えば(MoFedge::Data::DBIC::Schemaをつかってるので生のDBICとはちょっと違うかも)
package TestDBIC::Schema::Blog; use strict; use warnings; use base 'TestDBIC::Schema'; __PACKAGE__->table('blog'); __PACKAGE__->add_columns( qw/ id rid member_id title created_on timestamp / ); sub retrieve_rid { my $self = shift; my $rid = shift; return $self->search({rid => $rid})->first; } ... my $member = $self->model('Blog')->retrieve_rid('et5qZkSihU'); warn $member->id;
こんな感じでやったとしても
Can't locate object method "retrieve_rid" via package "DBIx::Class::ResultSet"
と、エラーになります。
これは当然で、$self->model('Blog')から取れるのはResultSetのオブジェクトなのですが
retrieve_ridを定義しているのはスキーマ側のオブジェクトだからです。
まあ、ぜんぜん違うところにメソッドを生やしてるのですね。
なので普通にはでけません。
じゃあRの拡張はできないのかというとそんなことはないです。でけます。
案1:ResultSetManagerを使う方法
package TestDBIC::Schema::Blog; ... __PACKAGE__->load_components(qw/ResultSetManager/); __PACKAGE__->table('blog'); __PACKAGE__->add_columns( qw/ id rid member_id title created_on timestamp / ); sub retrieve_rid : ResultSet { my $self = shift; my $rid = shift; return $self->search({rid => $rid})->first; } 1; ... my $member = $self->model('Blog')->retrieve_rid('et5qZkSihU'); warn $member->id;
このように
ResultSetManagerをload_componentsすることで
sub retrieve_rid : ResultSet { my $self = shift; my $rid = shift; return $self->search({rid => $rid})->first; }
こんな感じにCatalystぽく書けます。
メソッドのアトリビュートにResultSetを付けることによって
自動でResultSetで使えるようにしてくれます。
案2:自分でResultSetのクラスを作成する。
まず自分のResultSetを作成します。
package TestDBIC::ResultSet; use strict; use warnings; use base 'DBIx::Class::ResultSet'; sub retrieve_rid { my $self = shift; my $rid = shift; return $self->search({rid => $rid})->first; } 1;
テストなので微妙な名前空間においてますが気にせず。
スキーマのほうでは以下のようにします。
package TestDBIC::Schema::Blog; use strict; use warnings; use base 'TestDBIC::Schema'; __PACKAGE__->table('blog'); __PACKAGE__->resultset_class('TestDBIC::ResultSet'); __PACKAGE__->add_columns( qw/ id rid member_id title created_on timestamp / ); 1;
このように
__PACKAGE__->resultset_class('TestDBIC::ResultSet');
と、このスキーマで使うresultset_classを指定することができます。
案3:MoFedge::Data::DBIC::ResultSetRegisterを使う(very EXPERIMENTAL!)
オイラが作りました。EXPERIMENTAL!!
http://code.mfac.jp/trac/file/MoFedge-Data-DBIC-Schema/lib/MoFedge/Data/DBIC/ResultSetRegister.pm
package TestDBIC::Schema::Blog; use strict; use warnings; use base 'TestDBIC::Schema'; __PACKAGE__->load_components(qw/+MoFedge::Data::DBIC::ResultSetRegister/); __PACKAGE__->table('blog'); __PACKAGE__->add_columns( qw/ id rid member_id title created_on timestamp / ); __PACKAGE__->add_resultset_method( 'retrieve_rid' => sub { my $self = shift; my $rid = shift; return $self->search({rid => $rid})->first; } ); 1;
MoFedge::Data::DBIC::ResultSetRegisterを使うと
add_resultset_methodというメソッドが追加されるので
そいつで無理やりResultSetにメソッドを生やします。
ちなみにDBIx::Class以外の名前空間に作ったプラグインとかは
__PACKAGE__->load_components(qw/+MoFedge::Data::DBIC::ResultSetRegister/);
こんな感じでモジュール名の前に+を書いてあげるといいです。
MoFedge::Data::DBIC::ResultSetRegisterをなんで作ったかというと
ResultSetManagerがキョドル時があった為、嫌いになったからです(半分本当)
ちなみにMoFedge::Data::DBIC::ResultSetRegisterは
MoFedgeとか関係なく使えるので、使いたい人は普通に使えるはずです。
大体こんな感じかな。
分かりにくいかもしれませんがフォースを使って読み解いて下さいw