【CakePHP】Model::create() の使い方と注意点
去年のダイエットの成果が、今年 1/4 程戻ってしまった kimoto です。 よーし、また頑張るぞ!来年から!
さて、本日は CakePHP の小ネタ。
Model::create() を使う場合の注意点です。
Model::create() とは?
Model::create() は、一旦モデルの中身をリセットしてくれるメソッドです。
例えば、配列にデータを複数持ち、それをループで回して全て INSERT しようとした際に、ループ内で Model::save() をすると、2回目以降が UPDATE になってしまう、という事があります。
その場合、Model::save() の前に Model::create() を呼び出してやると、モデルの中身がリセットされ無事に全て INSERT される、という物です。
例えばこんな感じ。例では、DB にはプライマリーキーである id というフィールドがあり、それを指定しないことで INSERT をしようとしているとします。
// ユーザ配列
$users = array(
array('user_id' => 'yamada', 'name' => '山田', 'age' => '20'),
array('user_id' => 'suzuki', 'name' => '鈴木', 'age' => '25'),
array('user_id' => 'sato', 'name' => '佐藤', 'age' => '40'),
);
foreach ($users as $user) {
$this->User->save($user);
// id の無いデータを save する事で INSERT をしたいのだが、
// これだと2回目以降が UPDATE になってしまい、
// できるデータは1つだけとなってしまう
}
foreach ($users as $user) {
$this->User->create();
$this->User->save($user);
// 毎回 save の前に create するときちんと INSERT が発行され、データは3つできる
}
Model::create() をおまじない的に使ってはいけない
一度ループでのデータ保存でうまくいかなくて、その時にこの Model::create() に出会うと救世主のように思えてしまい、「Model::save() の前にはとりあえず Model::create() つけとけ!」みたいな思考に陥りがちです。ていうか以前陥りました。
が、おまじない的にとりあえず、で Model::create() を使ってしまうと、UPDATE したい場合に痛い目に会う事があるので注意が必要です。
それは、DB にはあるのに更新用のデータ配列に入ってないフィールドがある場合、DB からデフォルトの値を持ってきてセットしてしまうからです。
わかりづらい日本語ですみません、例えばこういうことです。
先ほどの user データにプレミアム会員である事を示す premium_flag というフィールドがあったとします。
そしてその値を変更できるのはサイト管理画面からのみで、ユーザ自身が触れるプロフィール編集画面からは変更できない値だとします。うんうん、よくありますよね、こういうの。
で、その premium_flag が DB 上でデフォルト false となっていた場合、むやみに Model::create をしてしまうとプレミアム会員でなくなってしまう事がある、という事です。
以下がユーザのプロフィール編集画面のコードだとします。このままでは、ユーザがプロフィールを編集保存すると、強制的にプレミアム会員ではなくなってしまいます。
// premium_flag のフィールドが無い配列
$users = array(
'id' => $id,
'user_id' => $user_id,
'name' => $name,
'age' => $age,
);
$this->User->create();
// この create() で premium_flag が DB デフォルトの false にセットされる
$this->User->save($user);
// premium_flag が false のまま UPDATE されてしまう!
事故を起こさないために
対策としては、「Model::save にちゃんと更新対象のフィールドである fieldList を渡してやる」というのがあります。とはいえ様々な事情からそれが難しい場合もあるかもしれません。
やはり、「そもそも INSERT を想定している場合のみ Model::create() する」というのが一番です。
ただ、INSERT と UPDATE を共通化している場合などもあるかと思います。その際は、プライマリーキーがあるかどうかを判断して、その結果により Model::create() をするようにすれば OK です。
先ほどのコードを例にすると、こんな感じにするといいかと。
$users = array(
'id' => $id,
'user_id' => $user_id,
'name' => $name,
'age' => $age,
);
if (!$user['id']) {
// プライマリーキーである id がない場合は INSERT なので create する
$this->User->create();
}
$this->User->save($user);