はちゅにっき

こっちのブログはまったり更新

DBIx::Skinny::Row の値を JSON で出力したい!

から始まった、Row クラスをちょっと変わった方法で拡張するお話。
すごく特異なケースだと思いますが。。。


JSON に書き出すときに、できれば Row クラスから直接

my $itr = $model->search('table_name1');
while (my $row = $itr->next) {
    my $json = $row->to_json; # JSON にする
}

って感じで書けるといい、よね?
そんなわけで、Skinny の "Row クラスの拡張" 機能を利用してみます。
詳しい説明は去年の Advent Calendar で!

Row クラスの拡張について
http://perl-users.jp/articles/advent-calendar/2009/dbix-skinny/13.html

で、書いてみたのがこんな Row クラス。

package MyApp::Model::Row::TableName1;
use strict;
use warnings;
use base qw/ DBIx::Skinny::Row /;

use JSON ( );

sub to_json
{
    return JSON->new->utf8->encode(shift->get_columns);
}

1;

これで、目的を達成することができました。
Skinny べんり!かんたん!わーい!


い?
って、あれ?Row クラスってテーブル固有のものだから、すべてのテーブルで to_json を利用したい場合、テーブル数分の Row クラスを用意しなきゃダメなのかな?
テーブルが全部で 20個くらいあるんだけど、どうしよう。。。
こんなときのために、すべてのテーブルで共通して使える Row クラスを定義できるといいなー。
さてさて、どうしよう。。。

といっても Skinny の構造は PHP に移植する過程でだいたい理解しているつもりなので、なんとなく目星をつけて、"Row クラスの拡張" を実現している部分に手を加えてみることにしました。


本来 Skinny では MyApp::Model::Row::TableName みたいな特定のテーブル専用のクラスを、自動で use してみて、成功すればそのクラスを返し、失敗した場合には anon row class と呼ばれている(?) "MyApp::Model::Row::Cdb3ee45d06b8098ef489e14b9bbb801741e549c8" のような Row クラスを返します。
そこで、今回はこの部分に手を加えて

  1. テーブル専用 Row クラスの use を試みる
  2. 失敗したらテーブル共通 Row クラスの use を試みる
  3. 失敗したら anon row class を返す

となるようにしてみました。
といっても、Skinny に直接手を加えるのもアレなので、MyApp::Model クラスの方で、Row クラスを作っているメソッド (_mk_row_class) を再定義してあげました。
こんな感じ。


共通で利用する Row Class のクラス名が、テーブル専用の Row Class とかぶってしまうと、とてもマズイので、Camelize された後に絶対出てこないクラス名 *1 にしてみました。
"共通で" って言っているんだし "COMMON_ROW" とかの方がよかったのかな。。。
これで、テーブル数分の Row クラスを用意するのではなく、以下のような Row クラスを1つ作るだけで、すべての Row クラスに "to_json" メソッドを生やすことができるようになりました。

package MyApp::Model::Row::BASE_ROW;
use base qw/ DBIx::Skinny::Row /;
use JSON::XS ( );

sub to_json
{
    return JSON->new->utf8->encode(shift->get_columns);
}

1;

テーブル専用の Row クラスも欲しい場合には ↑ を継承して作ればオッケーっぽいよね。


さてさて、目的はひとまず達成したように思いますが、こんなやり方は正しいのかな?
ひとまず、わーい。

*1:テーブル専用の Row Class は必ず Camelize されるため