DBICでUser->find_by_id(1)とかやってみた
追記
改良版書きました
DBICでUser->find_by('age', 1)とかUser->find_all_by('user_id', 1)とかやってみた
catalystアプリの中で、DBICを使ってDBに問い合わせする部分をActiveRecordっぽくしてみた。
before
my $user = $c->model('DBIC::Users')->search({'me.id' => 1, 'me.deleted' => 0},{prefetch => [qw/book/]})->next;
after
my $user = User->find_by_id(1);
動機
モデル名を単数形にするのは神がやってたのをパクらせてもらった。
なのでUserで$c->model('DBIC::User')を呼ぶところだけ。
やってみよう
最初にやってみたのはこんなの。
- MyApp::Controller::Root.pm
package MyApp::Controller::Root; use strict; use warnings; use base 'Catalyst::Controller'; __PACKAGE__->config->{namespace} = ''; sub p {warn Dumper shift} sub auto :Private { my ( $self, $c ) = @_; *User = sub { $c->model('DBIC::User') }; p User->find(1)->id; # 1が返る return 1; }
駄目な点
- 2回目以降のアクセスではSubroutine MyApp::Controller::Root::User redefinedが発生
- no warnings 'redefine'するのは嫌
- テーブルの数だけこれ書くんかい?
- しかも全コントローラに?
改善する上で問題となる点
- なんとか外部に定義したい。しかし、定義にはどうしても$cが必要。
やってみよう・2回目
定義を一箇所に書いてみよう
- MyApp.pm
use Catalyst qw/ 〜〜〜 +MyApp::Utils /;
- MyApp::Utils.pm
package MyApp::Utils; use strict; use warnings; use DirHandle; use base qw/Exporter/; my @schemas; use subs @schemas; our @EXPORT = @schemas; BEGIN { my $path = $ENV{APP_HOME} || ''; # apache使用時のため $path .= './lib/MyApp/Schema'; # 本当は$c->path_toを使いたい my $d = DirHandle->new($path); while (defined (my $f = $d->read)) { next if $f =~ /^\./; $f =~ s/\.pm//; push @schemas, $f; } } sub setup { my $c = shift; $c->NEXT::setup(@_); foreach my $f (@schemas) { no strict 'refs'; *{$f} = sub { $c->model("DBIC::$f") }; *{'MyApp::ResultSet::' . $f . '::find_by_id'} = sub { my ($self, $id) = @_; $f = lc $f; $self->search({"$f.id" => $id})->next; } } } 1;
- MyApp::Controller::Root.pm
package MyApp::Controller::Root; use strict; use warnings; use MyApp::Utils; 略
改善した点
- use subsでredefined対策
- コントローラで書いてたのをMyApp::Utilsに定義するようにした、コントローラではこれをuseするだけでよい
- テーブル名はMyapp/Schema以下から取得するようにした
- find_by_idを自動で生やした
駄目な点
- Utilsで$cを使うためだけにMyAppでuseして、なおかつコントローラでもuseするってどうよ
- Utilsって名前の割にUtilじゃなくね?センス狂ってね?
Myapp/Schema以下からテーブル名取ってこれるのはこんな構成にしてるため
最近のcatalyst構成 - だるろぐ跡地
でもいい感じに出来てきた。
やってみよう・3回目
- MyApp::Utils.pm
MyApp::ActiveRecord.pmにリネーム。
- MyApp.pm
use Catalyst qw/ 〜〜〜 - +MyApp::Utils + +MyApp::ActiveRecord /;
- MyApp::Controller::Root.pm
- use MyApp::Utils + use MyApp::ActiveRecord
- MyApp::Schema::User.pm
__PACKAGE__->resultset_attributes({ alias => 'user', from => [{user => 'users'}], where => {'user.deleted' => 0}, cache => 1, prefetch => [qw/book/], });
終わり。
作成にあたり、神の残したコードにお世話になり、元・神様にヘルプしまくりました。感謝。