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 などは作成しなくても動く。(後から考えたら当たり前だった。)

