課題
CakePHPは save()
でインサートもアップデートも行います。
エンティティのidがあれば、インサート、なければアップデートが行われます。
ログやプロセス、ステータスなどを記録する機能で、インサートのみを期待するDB:テーブルに対して、例えばボタンの連打などによる、"連続save"でアップデートが発生していました。
[03/11 追記]
例えばアクセスログなどの場合、重複したデータのログをとることは出来ないので、適していないというご指摘をうけました。その通りです。ご注意くださいませ。
解決策
1. DB側で制約を設ける(ARCHIVEストレージエンジン/複合ユニークキー)
- ARCHIVEストレージエンジン良いけどインデックス欲しいしなー。使ったこと無いし心配...。
- cakeの仕様にあわせるためにDB構造いじるって...!?
- かなりレコードがたまるテーブルでidとcreatedの複合ユニークって大丈夫?
2. save時に$entity->idがあるかどうか確かめる
- saveしている全箇所修正か...。
3. cakeのテーブル or エンティティで共通で処理する
- これができればベスト
テーブル or エンティティで共通で処理する
アプリケーションルール
cakeのテーブルファイルで実装できる buildRules メソッドは、save前にバリデーションをかけることができます。
アップデート処理時のバリデートを常にfalseを返すようにしてみる
/*
* @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* @return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules)
{
$rules->addUpdate(function($entity, $options) {
return false;
}, 'ruleName');
return $rules;
}
うまくいきました!
※下記失敗例
/*
* @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* @return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique(['id']));
return $rules;
}
これはうまくいきませんでした。そもそもidはprimary_keyとしてユニークがかかっているわけだし...
謝辞
本件は、 slack の cakephp公式チームの japaneseチャンネルにて、
相談させて頂いたものをまとめたものです。
この場を借りて改めてお礼申し上げます。
@chinpei215 さん、@icchii さんありがとうございます!!
[CakePHP公式の日本語話者向けSlackチャンネル開設のご案内]
http://qiita.com/chinpei215/items/3c116171c5308365c314