DBIC::Schema で昔やっていたこと

Yappo さんのYappoLogs: DBIx::Class::Schemaの使い方 を読んで,空気を読まずに自分語りしたくなったので書きます。

最近めっきり DBIC 使っていないんで*1アレですが,make_schema_at とか知らない(というかおそらく存在しなかった)時代にやっていた方法。

当時のターゲット DBMSPostgreSQL でした。

  1. DBDesigner4 でスキーマをデザインする
  2. DBDesigner4 で MySQL 用の SQL をエクスポートする
  3. SQL::Translator で上記 SQL 文から PostgreSQL 用の SQL を作成→DB作成
  4. SQL::Translator で上記 SQL 文から DBIC::Schema 継承クラスの HogeHoge::Schema を作成
  5. HogeHoge::Schema::Relations というモジュールを作り,そこでリレーションや inflate / deflate カラムを設定
  6. HogeHoge::Model::* というモジュールを作り use HogeHoge::Schema::Relations して HogeHoge::Schema::* を継承して便利メソッドを追加
  7. アプリ本体からは HogeHoge::Model::* を use して使用する

アプリ仕様がこなれてくると 5 〜 6 のリレーションや inflate / deflate まわりは変わらなくなってくるので,3 〜 4 は Makefile を書いて対応してました。

あと,5 のリレーションの設定を手書きで書いていると気が滅入るので,

package HogeHoge::Util::RelationBuilder;
use strict;
use Carp;

sub define_relations {
    my $class = shift;
    my $base  = shift;
    my %args  = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;

    while (my ($src, $rels) = each %args) {
        if ($src !~ m'::'o) {   # relative class
            $src = "${base}::${src}";
        }

        while (my ($rel, $dsts) = each %$rels) {
            while (my ($acc, $param) = each %$dsts) {
                my ($dst, $field);

                if (ref $param eq 'HASH') {
                    $dst   = $param->{class};
                    $field = $param->{field};
                }
                elsif (ref $param eq 'ARRAY') {
                    $dst   = $param->[0];
                    $field = $param->[1];
                }
                else {
                    croak 'bad parameters';
                }

                if ($rel ne 'many_to_many') {
                    if ($dst !~ m'::'o) {   # relative class
                        $dst = "${base}::${dst}";
                    }
                }

                $src->$rel($acc, $dst, $field);
            }
        }
    }
}

1;

みたいなユーティリティモジュールを書いて*2

package HogeHoge::Schema::Relations;

use HogeHoge::Schema;
use HogeHoge::Util::RelationBuilder;

HogeHoge::Util::RelationBuilder->define_relations(
    'HogeHoge::Schema', {

        'Member' => {
            has_one => {
                'address' => {
                    class => 'Address',
                    field => 'member_id'
                },
            },
            has_many => {
                'books' => {
                    class => 'Book',
                    field => 'member_id'
                },
            },
        },

        'Book' => {
            belongs_to => {
                'member' => {
                    class => 'Member',
                    field => 'member_id',
                },
            },
        },

    },
);

1;

こんな記述をするようにしてました。ほんとは has_one / has_many / might_have の場合には,自動的にリレーションを逆貼りしたり,YAML ファイルから読み込むようにしたりするつもりだったんですが時間がなくて結局このままでしたね。


これでメンテナンサビリティーはどうかというと,スキーマの記述が分散してしまっているんで,普通に組むより落ちているような気もしますが…スキーマをグラフィカルにデザインできるというのも中〜大規模スキーマにおいては有用だったかな,と自分を納得させてます。

しかし,今ならぜってーこんなことしねぇーーー

*1:Class::C3 が昔よりだいぶん速くなったらしいんで改めて DBIC をがりがり使ってみたいですね

*2:今見返すと each 使ってるのが見やすいとはいえアレだなぁとか色々思いますね