こんにちは、Research Panel Asiaの大山です。 今回は、Perlで最近ホットなO/Rマッパ、 Teng の便利な使い方を共有いたします。

みなさんは、

sqlite> .tables
user_log_2013_03  user_log_2013_04
user_log_2013_05  user_log_2013_06
user_log_2013_07  user_log_2013_08
こんな要領で、定期的にDBにテーブルが作られていくような仕組みを見たことはあるのでしょうか。 最近のMySQLでは著しくパーティショニング機能が改善されていますが、少し前のバージョンを使用していると 制約と制限 の都合で選びにくかったり、テーブルが巨大になるとスキーマの変更が容易ではなくなるなどの事情で、このような構造を選択する開発現場もあるのではないかと思います。

こういった、決まったスキーマの別名テーブルを扱う状況では、O/Rマッパの実装次第ではなかなか面倒なことになります。素SQL+DBIで実装するのも当然有効な選択肢と思いますが、Tengを使うととても簡単に解決することが可能です。

[MyModel.pm]


package MyModel;
use parent qw(Teng);
1;

[MyModel/Schema.pm]


package MyModel::Schema;
use strict;
use warnings;
use Teng::Schema::Declare;

sub get_table {
    my ($self, $table_name) = @_;
    $table_name =~ s/\d{4}\_\d{2}$/xxxx_xx/;
    $self->SUPER::get_table($table_name);
}

sub get_row_class {
    my ($self, $table_name) = @_;
    $table_name =~ s/\d{4}\_\d{2}$/xxxx_xx/;
    $self->SUPER::get_row_class($table_name);
}

table {
    name 'user_log_xxxx_xx';
    pk 'id';
    columns qw( id hoge fuga );
    row_class 'MyModel::Row::UserLog';
};

1;

Teng::Schema の中で、テーブルと行オブジェクトを扱うところの実装がとても単純になっているので、 このように get_table と get_row_class の2つのメソッドをオーバーライドしてあげるだけで、 扱うテーブル名によってちょっとした挙動の変更ができます。

こうすると、


my $handle = MyModel->new({ connect_info => ['dbi:SQLite:']});

my $item1 = $handle->insert(
    user_log_2013_07 => { hoge => 'hoge' }
);
# => MyModel::Row::UserLog インスタンス

my $item2 = $handle->insert(
    user_log_2013_08 => { hoge => 'hoge' }
);
# => MyModel::Row::UserLog インスタンス

$item1->update({ fuga => 'fuga' });
$item2->update({ fuga => 'fuga' });

$item1->delete;
$item2->delete;
といった具合に、いつものような行オブジェクトの生成ができるので、あとは MyModel::Row::UserLog のようなところに実装を追加していけばよいでしょう。 簡単ですね。

それでは、またお会いしましょう。ごきげんよう。