[PHP] CakePHP - HABTM(HasAndBelongsToMay)と中間テーブルの罠
前提 - 罠にはまるひと
CakePHPで、こんなことをしているひとは罠にはまります。
- モデル間アソシエーションとしてHABTM(HasAndBelongsToMany)を使っている
- HABTMの中間テーブルをモデルとして使っている
図にすると以下のような感じです。
ModelAとModelBが多対多の関係にあります。
CakePHPで多対多を実現するときは、中間テーブルを使います。
本来であれば、中間テーブルにはModelAとModelBの主キーだけを格納しておくべきなのですが、普通にモデルとして使ってしまう場合です。
+--------+ n n +--------+
+ ModelA +---+---+ ModelB |
+--------+ | +--------+
|
+-----+-----+
| Assoccor |
+-----------+
罠の内容
CakePHP 1.3時代からあるネタのようです。
HABTMを持つモデルのfind等を呼び出すと、中間テーブルに対するモデルがAppModel
のインスタンスとして生成されてしまい、自前で用意した中間テーブルに対するモデルが使われない。
何を言っているかわからn(ry
上の例で説明しますと、
ModelA#find
やModelB#find
を呼び出すと、Assoccor
クラスのインスタンスではなく、AppModel
のインスタンスが、内部的なレジストリに登録されてしまいます。
ここでAssoccor
クラスを使おうとすると、AppModel
のインスタンスがやってきます。
コードで書くと、このような感じです。
SomeController.php
class SomeController {
var $uses = array('ModelA', 'Assoccor');
public function doSomething() {
// ...
$result = $this->ModelA->findAll();
// ↑ ここで中間テーブルに対するモデルのインスタンスも生成されている
// ...
$this->Assoccor->someSpecialMethod();
// ↑ AppModelのインスタンスになっているので、
// メソッドが見つからないというエラーになる
}
}
解決策
アソシエーションを定義するとき、with
パラメータで中間テーブルに対するクラス名も渡してあげます。
array('className' => 'ModelB',
'joinTable' => 'model_a_model_b',
'with' => 'Assoccor'
);