CakePHPでデータを一括登録する

CakePHPでちょっとハマったので、メモ

 

CakePHPのモデルを使うと、SQL文を書かなくてもレコードの登録や更新ができます。

例えば、citiesというテーブルがあるとします。


CREATE TABLE `cities` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `post_code` char(7) NOT NULL,
  `name` tinytext NOT NULL,
  PRIMARY KEY(`id`),
  UNIQUE KEY `post_code` (`post_code`)
);

このテーブルにレコードを登録する場合は、Cityというモデルクラスを用意して、

※テーブル名が英語の複数形で、モデルクラス名が単数形なのは、CakePHPの既定の命名規則で、この規約に従って命名すると、いろいろ記述を省略できて便利なんですが、この規約に従わないように記述することもできます。


class City extends AppModel {
}

コントローラのメソッド内でCityクラスのsaveメソッドを呼んでやればいいです。


$data = array(
  'City' => array(
	'post_code' = '0012345',
	'name' = '山田市小川町'	
  );
);
$this->City->save($data);

この時save()に渡す連想配列に、プライマリキー(id)が含まれていれば更新、含まれていなければ追加されます。

 

コレを使って、CSVファイルから、一気にcitiesテーブルへ登録する処理を書いてみました。


$fp = fopen($csv_file, 'r');
while ($fields = fgetcsv($fp)) {
	$post_code = $fields[0];
	$name = $fields[1];
	$data = $this->City->find('first', array(
				'conditions' => array(
					'post_code' => $post_code
				)
			)
		);
	$data['City']['post_code'] = $post_code;
	$data['City']['name'] = $name;
	$this->City->save($data);
}
fclose($fp);

更新する前に、郵便番号(テーブル内でユニーク)で検索して、各フィールドの値を上書きして、書き戻すようしています。 これだと、レコードが見つかったら、idが含まれているので更新、見つからなければ追加となるはずです。

 

...しかし、うまくいきません。レコードが一件も登録されていない状態で実行すると、CSVファイルから読み込んだ1行目のデータが追加登録されるところまではうまくいきますが、2件目以降のデータが追加ではなく、最初のレコードに更新されてしまい、最終的には、CSVファイルの最後の行の値で更新された1件だけのレコードが登録された形になります。

save()に渡す連想配列にidが含まれていないにもかかわらず、追加ではなく、前回処理したレコードのidで更新されてしまっているようです。

最初原因が全くわからず、CakePHPのバグか!とまで思ってしまったのですが、公式サイトのドキュメントに書いてありました。(;´д`)

連続してテーブルを更新するような場合は、Model::create()を呼んで、状態をリセットする必要があるとのこと。

$this->City->find(〜)の前に、$this->City->create()を呼ぶようにしたら、意図通りに動作しました。