DBIC のスキーマをモデルクラスとして使ってみる
ドキュメントに書いてないので今後も使えるかわかんないけど、メモメモΦ
普通 DBIC で新しいレコードを挿入するには、
use strict; use warnings; package My::Schema::Artist; use base qw( DBIx::Class ); __PACKAGE__->load_components(qw( Core )); __PACKAGE__->table('artist'); __PACKAGE__->add_columns( id => { data_type => 'INTEGER', is_nullable => 0, is_auto_increment => 1, }, name => { data_type => 'VARCHAR', is_nullable => 0, }, ); __PACKAGE__->set_primary_key('id'); package My::Schema; use base qw( DBIx::Class::Schema ); __PACKAGE__->register_class('artist', 'My::Schema::Artist'); my $schema = My::Schema->connect('dbi:SQLite:test.db'); $schema->deploy(); my $new_artist = $schema->resultset('artist')->create({ name => 'dayflower', });
みたいに、resultset()
の create()
メソッドを使う。
本題と関係ないけど、SQL::Translator をインストールしてあると、上記のように connect()
して得られたスキーマインスタンスに deploy()
を発行することでテーブルを生成することができる。ちょいと便利ですね。
さて。
My::Schema::Artist
は一見普通のクラスなので、インスタンス化してみる。
my $schema = My::Schema->connect('dbi:SQLite:test.db'); my $new_artist = My::Schema::Artist->new(); $new_artist->name('dayflower');
うまく動く。
じゃあこれを INSERT できるかなと思ってやってみると、
$new_artist->insert(); # => DIED! # Can't call method "resolve" on an undefined value # at /usr/lib/perl5/site_perl/5.8.8/DBIx/Class/Row.pm line 1210.
できない。Storage とバインドされてないから(挿入先がわからないんで)当たり前ちゃあ当たり前。エラーメッセージだけからは原因がわかんないのはご愛嬌?
んで、レガシーコードでは、このモデルもどきインスタンスから resultset()
をひっぱりだして create()
するっていうユーティリティクラスを定義してたりした。
でも API やコードをみてみると result_source()
に設定すればいけるような気がした。
my $schema = My::Schema->connect('dbi:SQLite:test.db'); my $new_artist = My::Schema::Artist->new(); $new_artist->name('dayflower'); $new_artist->result_source($schema->source('artist')); $new_artist->insert(); # => OK!
お、うまくいった。
ユーティリティメソッドを定義して
sub My::Schema::Artist::store_to_schema { my ($self, $schema) = @_; $self->result_source($schema->source($self->table)); if ($self->in_storage) { $self->update(); } else { $self->insert(); } } my $schema = My::Schema->connect('dbi:SQLite:test.db'); my $new_artist = My::Schema::Artist->new(); $new_artist->name('dayflower'); $new_artist->store_to_schema($schema); $new_artist->name('foo'); $new_artist->update();
とかするとそれっぽい。
で、これは使えるか?
単体で使うならアリだけど、リレーション張るとうまく使えない。
use strict; use warnings; package My::Schema::Model; sub store_to_schema { my ($self, $schema) = @_; $self->result_source($schema->source($self->table)); if ($self->in_storage) { $self->update(); } else { $self->insert(); } } package My::Schema::Artist; use base qw( DBIx::Class My::Schema::Model ); __PACKAGE__->load_components(qw( Core )); __PACKAGE__->table('artist'); __PACKAGE__->add_columns(qw( id name )); __PACKAGE__->set_primary_key('id'); package My::Schema::Album; use base qw( DBIx::Class My::Schema::Model ); __PACKAGE__->load_components(qw( Core )); __PACKAGE__->table('album'); __PACKAGE__->add_columns(qw( id title )); __PACKAGE__->set_primary_key('id'); My::Schema::Album ->belongs_to('artist', 'My::Schema::Artist', 'artist_id'); package My::Schema; use base qw( DBIx::Class::Schema ); __PACKAGE__->register_class('artist', 'My::Schema::Artist'); __PACKAGE__->register_class('album', 'My::Schema::Album' ); package main; my $schema = My::Schema->connect('dbi:SQLite:test.db'); my $new_album = My::Schema::Album->new(); $new_album->title('daily dayflower'); $new_album->artist->name('dayflower'); # => DIED! # Can't call method "resolve" on an undefined value # at /usr/lib/perl5/site_perl/5.8.8/DBIx/Class/Row.pm line 1210.
みたく、relationship の先にアクセスしようとするとうまくいかない。
インスタンスを設定すればいいかなと思って
$new_album->artist( My::Schema::Artist->new() );
ってやってもダメ(この時点で)。
relationship に基づくプロパティは Storage とバインドされてないと使えないみたい。
面倒だけど
my $schema = My::Schema->connect('dbi:SQLite:test.db'); my $new_artist = My::Schema::Artist->new(); $new_artist->name('dayflower'); $new_artist->store_to_schema($schema); my $new_album = My::Schema::Album->new(); $new_album->title('daily dayflower'); $new_album->artist_id($new_artist->id); $new_album->store_to_schema($schema);
みたいに、ひとつのテーブルずつやらなきゃいけない。こうなるとうまみがまるでない。