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()を呼ぶようにしたら、意図通りに動作しました。