Teng でリレーションを使う(メモ)
はじめに
いつもORM(O/Rマッパー)のDISられ具合がひどいですが自分的には大大好きなんです。何が好きかっていうとリレーション設定が簡単にできるとかinflateとかdeflateも自動でやってくれるし開発効率もグッと上がります。こんな便利なもの使わない方がおかしいと思うぐらいです。生のDBIをたたいて省メモリでecoなシステムを目指すのもいいですが一昔前に比べればマシンパワーも上ってる事だしどんどん使いましょうよ。
ちなみにいつも使ってるO/RマッパはDBIx::Classです。
そういえば昨日バージョンがTeng-0.14になったばかりでしたね
Tengから利用する基本クラスを定義
Tengを操作するクラスを定義します。 MyAppプロジェクトでTengを使う場合
package MyApp::Model; use parent 'Teng'; 1;
基本となるのはたったこれだけです。 あと必要なのはテーブル情報を入れたschemaクラスを定義してあげます。
schemaクラスを定義
package MyApp::Model::Schema; use Teng::Schema::Declare; table { name 'tweet'; pk 'tweet_id'; columns qw( tweet_id group_id text create_on user_id ); }; table { name 'user'; pk 'user_id'; columns qw( user_id profile_image_url name screen_name ); }; 1;
上記では2つのテーブル(user tweet)のテーブル情報を定義しています。 それぞれのカラム名やプライマリキーの設定を行います。
必要最低限の設定は揃ったのでデーターベースに接続してみます。
use MyApp::Model; my $teng = MyApp::Model->new( {connect_info => ['DBI:mysql:database=dbname;host=localhost;', '', '', {mysql_enable_utf8=>1}]});
これでデータベースにアクセスするベースクラスのtengオブジェクトが取得できます。このtengオブジェクトを使ってDB内にあるテーブルにアクセスすることが出来るようになります。
userテーブル一覧を表示
Tengオブジェクトに生えているsearchメソッドでuserテーブルの内容を取得するとイテレーターオブジェクトが帰ってくるので$itr->nextメソッドで順番にrowオブジェクトを取得する事ができます。
ここで取得できたrowオブジェクトにはuserテーブルに存在するカラム名と同名のメソッドがあり値を取得する事ができます。
my $user_itr = $teng->search("user", {}); while ( my $user = $user_itr->next ) { print join "|", $user->user_id, $user->name, $user->create_on; }
一意のuserを表示する
取得できるデータが1つしかないと分かっている場合Tengオブジェクトに生えているsingleメソッドを使います。
my $user = $teng->single("user", { user_id => 111 }); print join "|", $user->user_id, $user->name;
singleメソッドで取得できるのはrowオブジェクトになります。イテレーターではないのでnextメソッドは生えていません。 2つのテーブルのリレーションシップを設定する
2つのテーブルuser及びtweet間で関連するカラムuser_idを使って関連つけることができます。
例えばあるユーザーのtweetメッセージのみを取得したりすることが簡単に出来るようになります。
user→tweetのリレーションの場合1対多のhas_manyなリレーション
Tengでリレーション設定をするにはRowオブジェクトへのメソッド追加する方法で可能です。 基本となるクラスがMyApp::Modelな場合のRowオブジェクトクラスはMyApp::Model::Row::{Table}になります。
このRowオブジェクトクラスはTeng::Rowクラスを継承したクラスである必要があります。作成したRowクラスに任意のメソッドを追加することで簡単にリレーションの設定が可能になります。 例)任意のuserに関連したtweetを取得するto_tweetsメソッドを定義する
tweetテーブルから該当するuser_idを持つものを全て取得します。 searchメソッドでtweetテーブルを検索しています。返ってくるのはイテレーターオブジェクトになります。
package MyApp::Model::Row::User; use parent 'Teng::Row'; sub to_tweets { # has_many my $self = shift; $self->{teng}->search('tweet', { user_id => $self->user_id }); }
■使い方
my $user = $teng->single('user',{ user_id => 152032379},{}); my $tweet_itr = $user->to_tweets; print "Name: ", $user->name; while ( my $tweet = $tweet_itr->next ) { print "tweet:", $tweet->text; } 1;
例)tweetのrowオブジェクトに関連したuserを取得するto_userメソッドを定義する。
tweet→userの場合多対1のbelongs_toなリレーション
上と違うのはbelongs_toな多→1なので一意なデータを取得するのでsingleメソッドを使うことです。 singleメソッドから返ってくるのはrowオブジェクトになります。
package MyApp::Model::Row::Tweet; use parent 'Teng::Row'; sub to_user { # belongs_to my $self = shift; $self->{teng}->single('user', { user_id => $self->user_id }); } 1;
■使い方
my $tweet_itr = $teng->search('tweet', {}); while ( my $tweet = $tweet_itr->next ) { print "Name: ", $tweet->to_user->name , " tweet: ", $tweet->text, "\n"; }
※tweetのrowオブジェクトにto_userメソッドが生えてます。tweetしたuser->nameがリレーションで簡単に取得出来ますね。