超超手抜きORマッピング

全然ORマッピングと呼べるようなものでは無いのだけれど、無いよりはマシ(かもしれない)なやり方。気の利いたモジュールも使えず、時間もかけたくないし、ちょっとしたツール的なものだし、自作で気の利いた事をする気にもなれなかったから、下記のような超シンプルな手順でDB周りを実装した。

  1. DBIで普通にSQL(SELECT分)を実行
  2. 結果をハッシュのリストとして受け取る($sth->fetchrows_hashref)
  3. 各ハッシュを後で説明するnormalize_hash()で正規化
  4. うまー

で、normalize_hashっていうのは下記「元ハッシュ」のような平べったいハッシュを「変換後ハッシュ」のように階層化するだけ。

[元ハッシュ]
{
    book_id => 1,
    title => '我が輩は猫である',
    _author_author_id => 101,
    _author_name => '夏目 漱石'
}

[変換後ハッシュ]
{
    book_id => 1,
    title => '我が輩は猫である',
    author => {
        author_id => 101,
        name => '夏目 漱石'
    }
}

つまり、キーが下線(_)で始まる時(ここでは_author_author_idと_author_name)は最初の単語を抜き出して階層化している。これがなぜ嬉しいかというと次のようなSQLを投げてこのnormalize_hashで正規化すれば、階層化されたオブジェクトみたいなハッシュが出来上がるんだ。

<テーブル構造>
CREATE TABLE BOOK (
    BOOK_ID ...,
    TITLE ...
    AUTHOR_ID ...,
);

CREATE TABLE AUTHOR (
    AUTHOR_ID ...,
    NAME ...,
);

SELECT 
    B.BOOK_ID as book_id,
    B.TITLE as title,
    A.AUTHOR_ID as _author_author_id,
    A.NAME as _author_name,
FROM
    BOOK as B,
    AUTHOR as A
WHERE
    B.AUTHOR_ID = A.AUTHOR_ID

まぁ、幾つかのテーブルが1対1で連結するときは簡単に階層化されたハッシュに出来てちょっと嬉しい、みたいな。

で、最後になりましたが、normalize_hashのソースはこんな感じ(自作のクラスのメソッドになってるから$selfとか受け取ってるけど、重要じゃない)。ってかバグありそうな気がするんだけど、今のところ動作しているみたい。ちなみに2段以上の階層化はしてない。

sub normalize_hash
{
    my ($self, $hash) = @_;
    my %symbol_table = ();

    foreach my $key (keys %$hash) {
        if ($key =~ /^_(.*?)_(.*)$/) {
            my $symbol = $1;
            my $symbol_key = $2;

            my $table = $symbol_table{$symbol} || {};
            $table->{$symbol_key} = $hash->{$key};
            $symbol_table{$symbol} = $table;
        }
    }

    my %newhash = (%$hash, %symbol_table);

    return ?%newhash;
}