CakePHPのsaveメソッドでINSERTするつもりがUPDATEになってしまう場合

例えば $tags に array(‘foo’, ‘bar’, ‘baz’) と入っていて
これらのタグがデータベースへ未登録の場合に
INSERT するという作業を行いたいときは
Tag モデル内で

foreach ($tags as $tag) {
  if (!$this->findByTag($tag)) {
    $data['Tag']['tag'] = $tag;
    $this->save($data);
  }
}

こんな感じでよさそうなものだが、
これだと2周目以降がUPDATEになってしまう。

INSERT INTO `tags` (`tag`,`modified`,`created`) VALUES ('foo','2008-02-28 14:45:50','2008-02-28 14:45:50')
UPDATE `tags` AS `Tag` SET `Tag`.`tag` = 'baa', `Tag`.`modified` = '2008-02-28 14:45:50' WHERE `Tag`.`id` IN (1)
UPDATE `tags` AS `Tag` SET `Tag`.`tag` = 'baz', `Tag`.`modified` = '2008-02-28 14:45:50' WHERE `Tag`.`id` IN (1)

3周分のデータを確認しても

Array
(
  [Tag] => Array
  (
    [tag] => foo
  )
)
Array
(
  [Tag] => Array
  (
    [tag] => baa
  )
)
Array
(
  [Tag] => Array
  (
    [tag] => baz
  )
)

別に id は指定されていないのだが、
どうもうまくいかない。

というわけで id を無理矢理 null にしてみた。

foreach ($tags as $tag) {
  if (!$this->findByTag($tag)) {
    $data['Tag'] = array(
      'id' => null,
      'tag' => $tag,
    );
    $this->save($this->data);
  }
}

これで成功。

何だかあまりスマートじゃない気がするけど、
動かないよりはマシなのでこれで。

きっともっと賢い方法があると思うので
あとはいつもの他力本願。誰かよろしく!

追記

その後、よりそれらしい方法を教えていただいた。
@kosugi と、コメントをくれた方、ありがとうございます。

Model::create() でよかった。

cake/libs/model/model.phpの
create メソッドのコメントによると

Initializes the model for writing a new record, loading the default values for those fields that are not defined in $data.

($data で定義されていないフィールドにデフォルト値を読み込んで、新しいレコードを書き込めるようにモデルを初期化します。)

とのこと。

これで Model の状態がリセットされるので、
初回と同じく INSERT されるようになる。

というわけで、先ほどの処理はこんな感じかな?

foreach ($tags as $tag) {
  if (!$this->findByTag($tag)) {
    $this->create();
    $data['Tag']['tag'] =  $tag;
    $this->save($data);
  }
}

$this->create();

$this->save($this->data);
の前でも後でも結局は同じ結果だと思うが、
初期化するという意味では前の方が自然な気がする。

関連エントリ

関連書籍

CakePHP ポケットリファレンス (Pocket Reference)

  • このエントリーをはてなブックマークに追加

One Response to “CakePHPのsaveメソッドでINSERTするつもりがUPDATEになってしまう場合”

  • Anonymous

    2008/02/28 19:25

    私もはまりました
    ↓のようにcrate()でモデルを初期化するといいみたいです
    $this->Tag->create();
    $this->Tag->save($tagData);