
ORマッパーを全く知らない初心者がせっかくなのでOrmパッケージを使用してCRUDしてみるセクション。
Relating Modelsって?状態からスタート。対象はMySQLです。
usersとprofilesというテーブルを使って実演してみます。
ふたつのテーブルは
親 : users -> Has One
子 : profiles -> Belongs To
という関係です。
↓準備は以下。
CREATE TABLE IF NOT EXISTS `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `password` varchar(255) NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; CREATE TABLE IF NOT EXISTS `profiles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL, `nickname` varchar(255) NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; |
/fuel/app/classes/model/user.php
<?php namespace Model; class User extends \Orm\Model { protected static $_has_one = array( 'profile' => array( 'key_from' => 'id', 'model_to' => 'Model\Profile', 'key_to' => 'user_id', 'cascade_save' => true, 'cascade_delete' => true, ), ); protected static $_observers = array( 'Orm\\Observer_CreatedAt' => array( 'events' => array('before_insert'), 'mysql_timestamp' => true, 'property' => 'created_at', ), 'Orm\\Observer_UpdatedAt' => array( 'events' => array('before_save'), 'mysql_timestamp' => true, 'property' => 'updated_at', ), ); protected static $_properties = array( 'id', 'password', 'created_at', 'updated_at', ); } /* End of user.php */ |
/fuel/app/classes/model/profile.php
<?php namespace Model; class Profile extends \Orm\Model { protected static $_belongs_to = array( 'user' => array( 'key_from' => 'user_id', 'model_to' => 'Model\User', 'key_to' => 'id', 'cascade_save' => false, 'cascade_delete' => false, ) ); protected static $_observers = array( 'Orm\\Observer_CreatedAt' => array( 'events' => array('before_insert'), 'mysql_timestamp' => true, 'property' => 'created_at', ), 'Orm\\Observer_UpdatedAt' => array( 'events' => array('before_save'), 'mysql_timestamp' => true, 'property' => 'updated_at', ), ); protected static $_properties = array( 'id', 'user_id', 'nickname', 'created_at', 'updated_at', ); } /* End of profile.php */ |
CREATE
まずは挿入。users,profilesテーブル共にレコードを挿入します。
$user = new Model\User(); // $_has_oneで設定した値(この場合は profile)の配列キーにDBカラム名をセットします $user->profile = new Model\Profile(); $user->password = 'password'; $user->profile->nickname = 'Gibson'; $user->save(); |
実行されるSQLは
INSERT INTO `users` (`password`, `created_at`, `updated_at`) VALUES ('password', '2012-06-11 20:03:05', '2012-06-11 20:03:05'); INSERT INTO `profiles` (`user_id`, `nickname`, `created_at`, `updated_at`) VALUES (1, 'Gibson', '2012-06-11 20:03:05', '2012-06-11 20:03:05'); |
の2件。
Belongs Toで設定した値名を間違えると、以下のエラーメッセージが出ます。
例: $user->profil = new Model\Profile(); と記述した場合。
OutOfBoundsException [ Error ]: Property "profil" not found for Model\User.
UPDATE
usres.id = 1の各レコードを更新します。
$user = Model\User::find(1); $user->password = 'パスワード'; // $_has_oneで設定した値( この場合は profile)の配列キーにDBカラム名をセットします $user->profile->nickname = "Epiphone"; $user->save(); |
実行されるSQLは以下。
SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; SELECT `t0`.`id` AS `t0_c0`, `t0`.`user_id` AS `t0_c1`, `t0`.`nickname` AS `t0_c2`, `t0`.`created_at` AS `t0_c3`, `t0`.`updated_at` AS `t0_c4` FROM `profiles` AS `t0` WHERE `t0`.`user_id` = '1' LIMIT 1; UPDATE `users` SET `password` = 'パスワード', `created_at` = '2012-06-11 20:03:05', `updated_at` = '2012-06-11 20:05:47' WHERE `id` = '1' LIMIT 1; UPDATE `profiles` SET `user_id` = '1', `nickname` = 'Epiphone', `created_at` = '2012-06-11 20:03:05', `updated_at` = '2012-06-11 20:05:47' WHERE `id` = '1' LIMIT 1; |
READ
次に検索結果をリレーショナルなオブジェクトにしてみます。
検索対象はusers.id = 1のレコードデータです。
$user = Model\User::find(1); |
実行されるSQLは
SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; |
usersテーブルのレコードのみ抽出します。
両テーブルのレコードを抽出する場合は
$user = Model\User::find(1); $user->profile = Model\Profile::find(1); |
実行されるSQLは
SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; SELECT `t0`.`id` AS `t0_c0`, `t0`.`user_id` AS `t0_c1`, `t0`.`nickname` AS `t0_c2`, `t0`.`created_at` AS `t0_c3`, `t0`.`updated_at` AS `t0_c4` FROM `profiles` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; |
となります。
なお、更新直後にSELECTした場合(LAST_INSERT_IDを使用する場合?)は
Model\Userのfindを行ってもSQLは実行されませんでした。
$user = Model\User::find(1); $user->password = 'PassWord'; // $_has_one で設定した値の配列キーに DB カラム名をセットします $user->profile->nickname = "Fender"; $user->save(); $user = Model\User::find($user->id); |
実行されるSQLは
SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; SELECT `t0`.`id` AS `t0_c0`, `t0`.`user_id` AS `t0_c1`, `t0`.`nickname` AS `t0_c2`, `t0`.`created_at` AS `t0_c3`, `t0`.`updated_at` AS `t0_c4` FROM `profiles` AS `t0` WHERE `t0`.`user_id` = '1' LIMIT 1; UPDATE `users` SET `password` = 'PassWord', `created_at` = '2012-06-11 20:03:05', `updated_at` = '2012-06-11 20:09:54' WHERE `id` = '1' LIMIT 1; UPDATE `profiles` SET `user_id` = '1', `nickname` = 'Fender', `created_at` = '2012-06-11 20:03:05', `updated_at` = '2012-06-11 20:09:54' WHERE `id` = '1' LIMIT 1; |
となります。
※この場合、変数$user->idがLAST_INSERT_IDとなります。詳しくはコチラ。
プロファイラの結果から確認したところ
- 更新があった場合は [save()前に実行している]SELECT + UPDATEクエリ
- 更新がなかった場合は[save()前に実行している]SELECTクエリのみ
が実行されました。オブジェクトを格納している変数に差異がないから、という事でしょうかね。
DELETE
最後に削除の場合、
$user = Model\User::find(1); $user->delete(); |
実行されたSQLは
SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` = 1 LIMIT 1; SELECT `t0`.`id` AS `t0_c0`, `t0`.`user_id` AS `t0_c1`, `t0`.`nickname` AS `t0_c2`, `t0`.`created_at` AS `t0_c3`, `t0`.`updated_at` AS `t0_c4` FROM `profiles` AS `t0` WHERE `t0`.`user_id` = '1' LIMIT 1; DELETE FROM `users` WHERE `id` = '1' LIMIT 1; SELECT `t0`.`id` AS `t0_c0`, `t0`.`password` AS `t0_c1`, `t0`.`created_at` AS `t0_c2`, `t0`.`updated_at` AS `t0_c3` FROM `users` AS `t0` WHERE `t0`.`id` IS null LIMIT 1; DELETE FROM `profiles` WHERE `id` = '1' LIMIT 1; |
の5件。
途中のIS NULL の SELECTはなんだろう..未調査です。
備考
クエリの結果は以下の形で取得出来ます。
\Debug::dump( $user, $user->id, $user->password, $user->profile->nickname, ); |
まとめ
それぞれのテーブルがHas One ⇔ Belongsというリレーショナルで関係なので当然ですが複数のテーブル操作に記述がひとつ、というのが慣れない感じがします。
なんだかんだでLEFT JOINやINSART ALLみたいなことが可能なのかな、と思ってましたが単純に複数クエリ投げてる形でした。まぁそのための relatedなんでしょうけども。
なんとなくORマッパーがつかめてきたかも。
