前提 - 罠にはまるひと

CakePHPで、こんなことをしているひとは罠にはまります。

  • モデル間アソシエーションとしてHABTM(HasAndBelongsToMany)を使っている
  • HABTMの中間テーブルをモデルとして使っている

図にすると以下のような感じです。
ModelAとModelBが多対多の関係にあります。
CakePHPで多対多を実現するときは、中間テーブルを使います。

本来であれば、中間テーブルにはModelAとModelBの主キーだけを格納しておくべきなのですが、普通にモデルとして使ってしまう場合です。

+--------+ n   n +--------+
+ ModelA +---+---+ ModelB |
+--------+   |   +--------+
             |
       +-----+-----+
       | Assoccor  |
       +-----------+       

罠の内容

CakePHP 1.3時代からあるネタのようです。

HABTMを持つモデルのfind等を呼び出すと、中間テーブルに対するモデルがAppModelのインスタンスとして生成されてしまい、自前で用意した中間テーブルに対するモデルが使われない。

何を言っているかわからn(ry

上の例で説明しますと、

ModelA#findModelB#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'
      );

参考リンク