cakephp, modifiedが自動更新しない問題の解決方法を解説cakephp, modifiedが自動更新しない問題の解決方法を解説

2010/01/28

cakephp(version = ‘1.2.3.8166′)で、Model->save()時にmodifiedが更新されない場合の解決方法をメモ。
一言でいうと、Model->save()前のModel->read()は要注意!ということです。またその際調べた、Model->read()の中身や、Model->create()についても少し解説。

前提

まず前提として、この問題に関して検索(cakephp modifeid 更新されない)してよく出てくる、

  1. DBのmodifiedカラムの設定はできている ( 「datetime default NULL」 になってる)
  2. POSTリクエストの値にmodifiedが存在しない
  3. $this->data["Model"]["modified"]=NULL なチカラワザ解決しない

とします。
私は過去に上2つの状況にも出くわしており(汗)、検索結果に出てくるみなさまの解説に助けられております。感謝感謝。それでも解決しないときには、$this->data["Model"]["modified"]=nullでしぶしぶ妥協していました。
が、今回は、妥協せずに、解決したエントリーになります。

状況

例として以下を想定します。

  • とある1レコードを、削除確認画面から削除完了画面に遷移し削除する処理とする
  • 削除は論理削除とする(deleteではなく、削除フラグを立てるだけのupdate)
  • 削除対象のレコードのidのみ、削除確認画面からPOSTで飛んでくる($this->data['Model']['id']で参照できる)
  • 削除するレコードの内容を完了画面で、viewで見せたい

そのときのソースがこう。仮にPostモデルとすると、

// 値をview用にセット
$post = $this->Post->read(null,$this->data["Post"]["id"]);
$this->set('post',$post);
// deleteflgフィールドの更新
$update["Post"]["id"] = $this->data["Post"]["id"];
$update["Post"]["deleteflg"] = 1;
// 更新処理
$options = array("fieldList"=>array("deleteflg","modified"));
$this->Post->save($update,$options);

まず、

  1. idを元にread()でviewに必要なデータを取得し、$this->set
  2. $update配列に値をセット
  3. idとdeleteflgフィールドのみ更新したいので、fileldListで指定して、save()で更新

という流れです。
で、これだと見事にmodifiedが更新されません。SQLはこんな感じ。

UPDATE `table_name` SET `deleteflg` = 1, `modified` = ‘2010-01-27 22:42:53′ WHERE `table_name`.`id` = 52

正確に言うと、modifiedは更新されてるけど、値がnow()じゃなくupdateまえと同じ値、という状態です。

何故か?

ポイントは、データの取得にread()を利用していること。この場合の解決方法は二つあって、

  1. read()を使わない
  2. create()する

1.read()を使わない

ソースは、例えばこう

// 値をview用にセット
// $post = $this->Post->read(null,$this->data["Post"]["id"]);
$post = $this->Post->findById($this->data["Post"]["id"]);
$this->set('post',$post);
// deleteflgフィールドの更新
$update["Post"]["id"] = $this->data["Post"]["id"];
$update["Post"]["deleteflg"] = 1;
// 更新処理
$options = array("fieldList"=>array("deleteflg","modified"));
$this->Post->save($update);

read()の代わりにfindById()を利用しています。

2.create()する

// 値をview用にセット
$post = $this->Post->read(array("id","title"),$this->data["Post"]["id"]);
$this->set('post',$post);
// deleteflgフィールドの更新
$update["Post"]["id"] = $this->data["Post"]["id"];
$update["Post"]["deleteflg"] = 1;
// 更新処理
$options = array("fieldList"=>array("deleteflg","modified"));
$this->Post->create();
$this->Post->save($update);

save()の直前にcreate()しています。
これら、どちらの方法でもmodifiedの自動更新ができました。さて、では、その原因はなんだったのか?

根本的な原因

save()メソッドの中身までは追いきれて無いのでこの先少々憶測もはいりますが、動きを見て言えることは、read()を利用すると、read()内部で$this->Model->dataに値がセットされ、save()時に引数が無い場合それが利用されることです。
つまり今回は、read()を呼んだ時点で$this->Post->dataに既存のレコード情報がセットされます。

// 値をview用にセット
$post = $this->Post->read(null,$this->data["Post"]["id"]);
pr($this->Post->data) // ←この時点でこの値にデータがセット

実際に、Model->read()を見てみます。(/cake/cake/libs/model/model.php 1002行目~)

function read($fields = null, $id = null) {
	$this->validationErrors = array();
		if ($id != null) {
			$this->id = $id;
		}
		$id = $this->id;
		if (is_array($this->id)) {
			$id = $this->id[0];
		}
		if ($id !== null && $id !== false) {
			$this->data = $this->find('first', array(
				'conditions' => array($this->alias . '.' . $this->primaryKey => $id),
				'fields' => $fields
			));
		return $this->data;
	} else {
		return false;
	}
}

$this->data に$this->find()の結果がセットされてますね。(ちなみに、同じものがreturnされています。)
で、$update配列にidとdeleteflgをセットしますが、idとdeleteflgフィールド以外は既存レコードの値がupdateに利用されます。そのため、セットされていないmodifiedが、$this->Post->dataにセットされた既存レコードの値になっていた、というわけです。
そこで、解決方法は、$this->Post->dataにmodifiedがセットされないこと、になります。もしくは、NULLにリセットでもOK。(だから、$this->data["Model"]["modified"]=NULL でも解決できるわけです)よって、

  • read()を使わないことで、$this->Post->dataにセットしない

で解決できたわけです。
で、もうひとつのcreate()のほうですが、

  • create()は、$this->Post->dataを初期化

という方法になります。
Model->create()をすこし見てみますと。(/cake/cake/libs/model/model.php 972行目~)

function create($data = array(), $filterKey = false) {
	$defaults = array();
	$this->id = false;
	$this->data = array();
・・・(以下、省略)・・・・

はい、$this->dataがarray()で初期化されていますね。つまり、$this->Post->dataが初期化されます。

結論

Model->save()の前のread()は気をつけろぉーーー!!
で。よく、save()の前にcreate()したほうがいいよとかってのを見かけますが、こういう構造だからなんですねー。なんとなくの理解で使うのがイヤだったのでcreate()を避けてましたがw
さ、これで、気持ちよくsave()でmodifiedの自動更新できるかな・・・・。何度もハマるので、まだ少し不安ではありますw

(なにか間違いがあれば、ご指摘ください)

タグ:

コメントをどうぞ