Perl Hackers Hub

第3回 DBIx::Classでデータベース操作(3)

この記事を読むのに必要な時間:およそ 3 分

Resultクラスの拡張

Resultクラス,ResultSetクラスは自分の好みに合わせて拡張できます。

カラムのinflate/deflate

$tweet->created_dateのようなつぶやきの日付を取りたい場合,カラムの値そのままではなくDateTimeオブジェクトを返してくれたらうれしいと思います。その機能を実現するのがカラムのinflate/deflate機能です。inflate(膨らませる)は文字どおりカラムのデータをPerlオブジェクトに変換する機能で,deflate(収縮させる)はPerlオブジェクトをカラムデータへ変換する機能です。

Resultクラス内で

__PACKAGE__->inflate_column('column_name', {
    inflate => sub {
        # カラムデータからオブジェクトを作って返す
    },
    deflate => sub {
        # オブジェクトからカラムデータを作って返す
    },
});

という定義をすると,このカラムのアクセサがオブジェクトのinflate/deflateを行ってくれるようになります。

# inflateされたPerlオブジェクトを返す
my $obj = $raw->column_name;

# Perlオブジェクトをカラムに入れる
$raw->column_name($obj);

このようにデータベースからPerlオブジェクトを取得したり,Perlオブジェクトからそのままデータを入れられるようになるため,便利に使える場面が多いでしょう。

たとえば,JSONデータを格納したjsonというカラムがあるとしましょう。これを自動的に入出力時にPerlオブジェクトと変換を行いたい場合は,次のように書きます。

use JSON;

__PACKAGE__->inflate_column('json', {
    inflate => sub { decode_json(shift) },
    deflate => sub { encode_json(shift) },
});
●日付のinflate/deflate

日付のような,よく使用するであろうinflate/deflateは,自前で定義しなくともDBIx::Class::InflateColumn::DateTimeというパッケージで定義されているため,それを利用すれば簡単に利用できます。

利用するのは簡単で,Resultクラスに

__PACKAGE__->load_components(qw/InflateColumn::DateTime/);

と1行書くだけです。これを書くとdata_typeがDATEもしくはDATETIMEなカラムは自動的にDateTimeへinflate/deflateされるようになります。また,DateTimeオブジェクトのタイムゾーンなどを指定したい場合は,カラム定義に指定するとそのタイムゾーンが使われます。

created_date => {
    data_type => 'DATETIME',
    is_nullable => 0,
    timezone => 'Asia/Tokyo',
},
●カラムの生データへのアクセス

inflate/deflateしている場合,アクセサ経由では常にPerlオブジェクトが返ってきてしまうため,カラムデータそのものにアクセスできないという問題があります。その場合でもget_column/set_columnメソッドを使用するとカラムデータに直接アクセスできます。併せて覚えておくとよいでしょう。

Result/ResultSetクラスに自分のメソッドを増やす

DBIx::Classが用意しているメソッドだけでなく,独自のメソッドをResultクラスやResultSetクラスに定義することもできます。ユーザオブジェクトにfollow関数を追加するには次のようにします。

# in My::Schema::Result::User
sub follow {
    my ($self, $target) = @_;

    $self->add_to_following_maps({
        target => $target->id
    });
}

このようにResultクラスやResultSetクラス上で独自のメソッドを定義すると,それをSchemaオブジェクトから取得したResultクラスなどでそのまま使用できます。

# daisukeというユーザをfollow
my $daisuke = $user_rs->find({ username => 'daisuke' });
$user->follow($daisuke);

組み込みのメソッドを拡張する

ResultクラスやResultSetクラス内では独自のメソッドを定義できるだけではなく,DBIx::Classが用意しているメソッドをオーバーロードすることもできます。たとえばResultクラスの場合INSERT時に呼ばれるinsertメソッド,UPDATE時に呼ばれるupdateメソッド,DELETE時に呼ばれるdeleteメソッドをオーバーロードすることでデフォルトの挙動を変更したり,処理を追加したりできるようになります。

たとえばTweetクラスのようにデータ作成時刻を記録するcreated_date カラムと修正時刻を記録するmodified_dateカラムを持つテーブルがあったとしましょう。これらは次のようにするとそれらの時刻フィールドを自動的に更新されるようプログラムすることができます。

sub insert {
    my $self = shift;

    my $now = DateTime->now;
    $self->created_date($now);
    $self->modified_date($now);

    $self->next::method(@_);
}

sub update {
    my $self = shift;
    $self->modified_date(DateTime->now);

    $self->next::method(@_);
}

next::methodというメソッド呼び出しは,この場所でDBIx::Class が定義しているメソッドを呼び出すという意味です。たいていの場合はこのようにnextメソッドを呼び,その前後に自分の処理を加えて,既存のメソッドに対し処理を追加するような使い方が多いと思います。

ただ,nextを呼ばずにDBIx::Classが定義している処理を完全に上書きすることもできます。

Resultクラス,ResultSetクラスで定義されているメソッドのすべてはこのようにオーバーロードすることができます。

著者プロフィール

村瀬大輔(むらせだいすけ,ハンドルネーム:typester)

1981年2月生まれ。2004年9月株式会社カヤックに入社。

カヤックでは自社サービス「こえ部」を担当する傍ら,ラボチームBM11に所属し「Ark」「nim」といったオープンソースプロダクトを開発。また「Shibuya.pm」や「YAPC::Asia」にスピーカーとして参加するなど,Perlプログラマとして活躍の場を広げている。

コメント

コメントの記入