cakephp で 履歴テーブル
テーブルの変更内容全てを履歴として残して欲しい。
こういった要望はしばしば見受けられるのでメモ。
基本的にcakePHPの命名規則にのっとって記述する。
cakePHP 2.3.10
DBの準備
例として users テーブルとその履歴テーブル his_users を用意する。
(履歴テーブルは必ず his_ を冠するルールにする。)
CREATE文はこちら。
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `created` datetime NOT NULL, `modified` datetime NOT NULL, `delete_flag` tinyint(4) NOT NULL, `name` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `his_users` ( `his_id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL, `created` datetime NOT NULL, `modified` datetime NOT NULL, `delete_flag` tinyint(4) NOT NULL, `name` text NOT NULL, PRIMARY KEY (`his_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
フィールドの構造は基本的に揃える。
ただし,本テーブルである users は主キーとして id を用いるが,
履歴テーブルである his_users は主キーを his_id とし,id にはインデックスはもたせない。
AppModelの準備
save()を使った保存方法は大きく2通り。
(1) save()の引数を利用する方法
$Model->save($data);
(2) set()を使用する方法
$Model->set($data); $Model->save();
ちなみに set($data1) した後に save($data2) した場合 $data2 が上書きされる。
$Model->set($hoge) を利用すると $Model->data が $hoge を保存しており,
$hoge に id の記述があると自動で $Model->id に idが入る。
$Model->save(); 実行後, $Model->data は消去されるが $Model->id は消去されない。
レコード新規追加の場合は $Model->id に主キーが代入されている。
これらの性質を利用して以下のようにオーバーライドする。
public function save($data = null, $validate = true, $fieldList = array(), $his_flag = true) { // セーブデータ取得 if ($data !== null) { $this->set($data); } // セーブ実行 if ($r = parent::save($data, $validate, $fieldList) && $his_flag) { // セーブされたデータを取得 $_data = $this->find('first', array( 'conditions' => array( $this->name . '.id' => $this->id ) )); // 履歴テーブルのモデルロード $his_name = 'His' . $this->name; if ($this->$his_name = @Classregistry::init($his_name)) { // 履歴テーブルへの保存 $this->$his_name->create(); $this->$his_name->primaryKey = 'his_id'; $_data[$his_name] = $_data[$this->name]; unset($_data[$this->name]); $this->$his_name->save($_data, $validate, $fieldList, false); } } // 終了 return $r; }
まとめ・気づき
(1) save() をオーバーロードしたら saveMay() などにも適用できた。
(2) HisUser.php などは作成しなくても動く。(後から考えたら当たり前だった。)