hasManyなどで関連づけたテーブルにシーケンスがない場合のsaveAll
以下のようにCakePHPのCookbookに関連したテーブルへの保存方法が載ってますが、
PostgreSQLのシーケンス部分でハマってしまったのでメモ。
saveAll(array $data = null, array $options = array())
次のいずれかの目的で使用します。 (a) 単一のモデルに、個別のレコードを複数記録する。 (b) あるレコードと同様に、関連したレコードも全て記録する。
あるモデルのデータを保存するとき、それに関連付いているモデルのデータも一緒に処理されるということを意識しておいてください。新しい Post とそれに関連した Comments を保存する場合、保存処理の実行中には Post と Comment モデルを両方とも使用します。
流れは以下のような感じ
- ModelA hasMany ModelB
- ModelBはModelAのキーであるmodel_a_idをもつ(外部キーがmodel_a_id)
- ModelAのシーケンス名はmodel_a_id_seq
- ModelBはModelAのデータに従って保存されるのでシーケンス無し
- ModelA->saveAll()を使ってModelAとModelBの両方を同時に保存したい
ModelAを書くとこんな感じ
<?php class ModelA extends AppModel { public $useTable = 'model_a'; public $primaryKey = 'model_a_id'; public $sequence = 'model_a_id_seq'; }
ModelBはこんな感じ
<?php class ModelB extends AppModel { public $useTable = 'model_b'; }
この状態で以下のようにすると失敗する(保存データは適当)
<?php // ModelA と ModelB に保存するデータ $data = array( 'ModelA' => array( 'field' => 'value', ), 'ModelB' => array( array( 'field1' => 'value01', 'field2' => 'value02', ), array( 'field1' => 'value11', 'field2' => 'value12', ), ), ); // ModelAにhasManyでModelBを関連づけ $modelA = ClassRegistry::init('ModelA'); $modelA->bindModel(array( 'hasMany' => array( 'ModelB' => array( 'foreignKey' => 'model_a_id', ), ), )); // ModelA と ModelB に保存 // $modelA->id をセットしていないためinsert処理 $modelA->saveAll($data, array('validate' => 'first'));
画面のエラーを見るとmodelBのシーケンスを使おうとして失敗している様子
SELECT currval('model_b_id_seq') as max
で、どうしたらいいのかなぁ?と少し考えたんですが、ModelBはModelAのmodel_a_idに従うので
同じシーケンス名を指定してあげればいいみたい。
ModelB(改)
<?php class ModelB extends AppModel { public $useTable = 'model_b'; public $sequence = 'model_a_id_seq'; // ModelAのシーケンス名と同じもの }
これで上手くいきました。ModelBだけ更新するってこともないのでこれでOKなはず。