CakePHPは簡単かつウェブサイトが楽に作れる大変ありがたいフレームワークですが、たまに嵌りポイントがあります。
使っていてハマった点を忘れないように纏めます。
##No.1 - patchEntity, save等で孫テーブルがうまく動かない(マージ/保存できない)
親-子-孫の関係になっているテーブルに対し、hasOne/hasManyも定義しているのに親テーブル追加/更新で孫まで上手く反映されない。子まではうまく動く、get/findもうまく動く、エラーは出ない。という場合。
例: Companies --(hasMany)--> Users --(hasMany)--> UserSettings
###可能性① associationの書き方が間違ってる
get()とは違い、ネストした中にもassociatedを再度書く必要がある。
#get()の場合
$query = $companies->get($id, [
'contain' => [ 'Users' => ['UserSettings'] ]
]);
#newEntity/patchEntityの場合
$company = $companies->newEntity($data, [
'associated' => [
'Users' => [
'associated' => ['UserSettings']
// ↑ここにも'associated'が必要
]
]
]);
*公式マニュアルにも書いてあるものの、まさかget/findと違うことはないだろと思って読み飛ばしがち
*参考にさせてもらったページ(https://qiita.com/zaramme/items/719f77480c5f0cde0ae1)
###可能性② Entityの$_accessibleに子/孫要素が入ってない
この指定がtrueになっていないと無視される。
bin bakeで自動で設定してくれるはずなものの、条件によっては?親側のEntityに書いてくれていない事があるようで、bakeしなおすか手動で追加が必要。
(普通にget/findしてアクセスするときと同様、hasOneなら単数形、hasManyなら複数形で子テーブルの名前を追加)
###可能性③ dirtyフラグが立ってない
newEntityやpatchEntityでエンティティ作成後、手動で何か追加/変更した場合、dirty()も呼ばないと更新対象にならない。(公式)
$company->author->name = 'Master Chef';
$company->dirty('author', true);
##No.2 - 更新処理で子/孫テーブルのデータが追記/作り直される(delete/insertされる)
例: Users --(hasMany)--> UserSettings
親テーブル更新時に子テーブルのデータもまとめて更新したいが、親はupdate処理されるものの、子テーブルのデータはupdateしてくれず、追加(or deleteで一回消され、insertで新規保存)されてしまう場合。
(どっちになるかはsaveStrategy次第)
updateでないため子テーブルのprimary keyが変わってしまい、他との関係性が壊れる。
###可能性: 子/孫テーブルのprimary keyが渡されてない
Entityにprimary keyが無いとpatchしようがないので、追加/入れ直しになる。
ちゃんとあればupdate処理になる。
*patchEntityに渡すデータ(フォームからの入力、get/findで取ってくるデータ)の両方にprimary keyが必要。
つまりパッチ元のget/find時にも更新したい関係テーブルをcontainで指定しておく必要がある。
##No.3 - 単数/複数形の規約で混乱する
###単数/複数の変形
規約で使い分けた気持ちは分かるけど不規則の場合はどうする?
実際のCakePHPの変換を出してくれるサイトを参照。(古い?)
最新Verで動いているcakePHP公式のサイトもあり。
pokemonは単複同型です。
###DBからcontainで纏めて取ってきた子/孫テーブルの形は?
例: Companies --(hasMany)--> Users --(hasOne)--> Addressies
hasOne, belongsToは単数形、そのまま要素にアクセスできる。
$user->address->street
←addressと単数形になる
hasMany, belongsToManyは複数形で配列。
$company->users[0]->name
##No.4 - 関連テーブルでのwhereで失敗する
そんなカラム無いといわれて失敗する。
hasMany先相手だとSQLが分割されるが原因で、普通にwhereに書くだけではダメ。
目的によってcontainにfunctionを指定、queryBuilderを渡す、matchingを使ったりする。
マニュアル参照
*参考
##No.5 - hasMany先の特定の1個だけくっつけて取得したい
StackOverflowのここが分かりやすい。
サブクエリで取ってくる「Join strategy - Using a subquery for the join condition」が個人的に好み。
##No.6 - DebugKitが動かない
マニュアルの通りではあるけど
###可能性① forceEnableしてない
安全のためローカル環境(localhostとか)出ないと動かないようになっている。
それでも動かしたい場合はマニュアルの通りforceEnableを設定する。
これが嫌なら、hostsにテスト環境をmytest.localとかで登録する手もある。
###可能性② pdo_sqliteが無い
デフォルトでSQLliteを使ってるので、pdo_sqliteが無い場合は入れてやる必要がある。
普通にlogs/error.logに出るけどブラウザからは特に何も言われないので。