Rowの拡張について2 #21B!

こんにちは。walf443の443はSSLのポート番号のことですか?とよく聞かれるよしみです。

21日目は、Rowの拡張についてもうちょっとツッコんで話をしてみようと思います。

Proj::DBをDBIx::Skinnyのベースクラスとした場合に、Proj::DB::Row::TableNameみたいなクラスを作れば、singleやsearchなどの結果でjoinしていないときは、

Rowクラスが自動で適用されるようになるというのは前回お話したとおりかと思います。

Skinnyの思想的にはなるべくSQLをチューニングして書くことを意識しているので、CDBIや、DBICでやられているようなhas_a, has_manyみたいなものはあえて実装されていません。

とはいえ、アプリケーションの全ての箇所をカリカリにチューニングする必要はないと思いますし、チューニングと開発速度はトレードオフになることもあるので、

できるだけ早く新サービスをリリースしたいときのような開初初期段階にはお手軽なhas_aなどを使いたいといったこともあるかと思います。

というわけで、そういうのをRowに生やしたいときの例を示してみようかと思います。

package MyTest::Model::Schema;
use DBIx::Skinny::Schema;

install_table user => schema {
    pk 'id';
    columns qw/
        id
        name
        leader_user_id
        /;
};

1;

package MyTest::Model::Mixin::Row::HasA;
use strict;
use warnings;

sub import {
    my $pkg = caller;
    my $code_hasa = sub {
        my ($class, $method, $table, $column) = @_;

        my $code = sub {
            my $self = $_[0];
            $self->{"__cached_$column"} ||= $self->{skinny}->single($table, +{ id => $self->$column });
        };
        {
            no strict 'refs';
            no warnings 'redefine';
            *{"${pkg}::$method" } = $code; ## no critic
        }
    };

    {
        no strict 'refs';
        no warnings 'redefine';
        *{"${pkg}::has_a"} = $code_hasa; ## no critic
    }

}

1;

package MyTest::Model::Row::User;
use base qw(DBIx::Skinny::Row);
use MyTest::Model::Mixin::Row::HasA;

BEGIN {
    __PACKAGE__->has_a('leader', 'user', 'leader_user_id');
}

1;

みたいにしておくと、

use strict;
use warnings;
use Test::More;

my $db = MyTest::Model->new;

$db->create('user', {id => 1, name => 'alice'});
$db->create('user', {id => 2, name => 'bob', leader_user_id => 1 });
my $user = $db->single('user',{ id => 2});
my $leader = $user->leader;
is $leader->id, 1;
is $leader->name, 'alice';

みたいな感じにできます。

あまりRowにメソッドを増やしすぎると、column名と被ったりするとハマってしまいそうではありますが、

1プロジェクト内でRowにメソッドを増やすような場合は、テーブル名とかメソッド名やらに規約があったりして、

こういう仕組みを作っておくと、Row間で似たような処理を何度も書かなくてもよいよというお話でした。

これはあくまで例なので、お手軽に試せるように直接Rowから引けるskinnyを見にいっていますが、

あとからmemcachedやKVSから引くようにしたりしやすいので、Skinnyやcache処理などをwrapしたModel層を呼び出すような仕組みにしておくとよいでしょう。

have a nice skinny days!:)