November 28, 2011

Orm\Modelでリレーショナル型データベースを表現する。

2011/12/03 追記
こちらも併せて御覧ください。
http://madroom-project.blogspot.com/2011/12/ormmodelobservers.html



まず、以下2つのテーブルがあることとします。
usersが親。twitter_usersが子。
users.id = twitter_users.user_idの関係です。
create table users
(
    id int not null auto_increment,
    created_at int not null,
    updated_at int not null,
    primary key (id)
);
create table twitter_users
(
    id int not null auto_increment,
    created_at int not null,
    updated_at int not null,
    user_id int not null unique,
    twitter_user_id varchar(255) not null unique,
    twitter_screen_name varchar(255) not null,
    twitter_oauth_token varchar(255) not null,
    twitter_oauth_token_secret varchar(255) not null,
    primary key (id)
);
一見、1:1なので、usersにまとめられそうですが、その辺りは大人の事情です。
これをOrm\Modelで表現するにはどうするか。が今回の話です。


まず、親となるusersテーブルに対するモデル。
app\classes\model\user.php
namespace Model;

class User extends \Orm\Model
{

    protected static $_observers = array(
        'Orm\Observer_CreatedAt' => array('before_insert'),
        'Orm\Observer_UpdatedAt' => array('before_save'),
    );

    protected static $_has_one = array(
        'twitter_user' => array(
            'key_from' => 'id',
            'model_to' => 'Model\Twitter_User',
            'key_to' => 'user_id',
            'cascade_save' => true, // true/falseどちらで有るべきか、要調査。とりあえずはドキュメント通り。
            'cascade_delete' => true, // falseにすると、削除時にtwitter_userが削除されなかった。
        )
    );
}

次に、子となるtwitter_usersテーブルに対するモデル。
\app\classes\model\twitter\user.php
namespace Model;

class Twitter_User extends \Orm\Model
{
 
    protected static $_observers = array(
        'Orm\Observer_CreatedAt' => array('before_insert'),
        'Orm\Observer_UpdatedAt' => array('before_save'),
    );

    protected static $_belongs_to = array(
        'user' => array(
            'key_from' => 'user_id',
            'model_to' => 'Model\User',
            'key_to' => 'id',
            'cascade_save' => false, // trueにすると一切の変更がない場合のsave()でエラーとなる。
            'cascade_delete' => false, // trueにすると削除時に親も消してしまう。注意。
        )
    );
}

* 親のモデルは、子のモデルを$_has_oneで定義する。
* 子のモデルは、親のモデルを$_belongs_toで定義する。
みたいなニュアンスでしょうか。(果たして認識は正しいだろうか。。。)
尚、'cascade_save'と'cascade_delete'については、実は良くわかっていません。。。
とりあえずドキュメントのままです。(今度調べよう。名前からなんとなく憶測はできるけど。。。)


これで、例えば検索は
$user = User::find_by_id(1);
すると
$screen_name = $user->twitter_user->twitter_screen_name;
とか出来ます。
$user->twitter_userは、存在しない場合、nullになるようです。


saveする場合も、例えば
$twitter_user = new Twitter_User;
$twitter_user->user = new User;
して、
$twitter_user->twitter_user_id = [Twitterのuser_id];
や、その他のカラム値をセットして
$twitter_user->save();
すると、親を含めて一発登録出来ました。

追記:(上記ソースには以下の対処を反映済。)
上記の方法でsaveした時、一切の変更がない場合に、以下のエラーが発生しました。
Orm\FrozenObject [ Error ]: No changes allowed.
子のモデル(\app\classes\model\twitter\user.php)の$_belongs_toの
'cascade_save'をfalseに変更したところ、発生はしなくなりましたが、
一切の変更がない場合でも、updateが走るようになりました。
(updated_atのみが更新されます。)
とりあえず問題は無いですが、この辺りはしっかりと把握しておきたいなぁ。


参考:
http://docs.fuelphp.com/packages/orm/relations/belongs_to.html
http://docs.fuelphp.com/packages/orm/relations/has_one.html


P.S.
それぞれのモデルで$_observersというのがありますが、
これは、今回の話にはあまり関係有りません。。。

No comments:

Post a Comment