CodeLab技術ブログ

プログラミング技術まとめ

CakePHP2 Containableビヘイビアでモデルの抑制がリセットされてしまう問題

CakePHP2でどうもパフォーマンスが出ないと思っていろいろ調べていました。
findしたデータをダンプしてみたら問題発見、ある一部のモデルでcontainが効かなくなっていて以下のモデルすべてが出力されているのを発見しました。

原因

結構長時間、いろいろ試してみたのですが、どうやっても解決できず。
問題は、ちゃんとcontainが効くモデルと、効かないモデルがあるのが意味不明。
いろいろ試しているうちに、自作のビヘイビアを外したらなぜか問題なくなりました。

これをヒントにネットを検索したら以下のようなブログがヒットしました。

CakePHP2 でシャカリキに効率よく contain する [afterFind() という悪魔]

実は、件の自作ビヘイビアというのは、ログインユーザーのパーミッションによって読みこむレコードを自動的に制限するビヘイビアで、設定されたパラメータからconditionを自動生成して追加するものでした。
これの作り自体は問題がないのですが、問題はここでFindメソッドを読んでいたのが問題でした。
ContainableBehaviorを深く追ってないので、仕組みをよく理解していませんが、記事によるとどうも、内部でfindを呼ぶとアソシエーションをリセットする関数が呼ばれてしまうのが原因のようです。
たぶん、unbindModel()メソッドを使ってアソシエーションを解除しているんだと思いますが、これはfind実行後に元に戻すようになっているため、一度findを呼ばれるとリセットされるのだと思います。

記事ではafterFind()で問題があるとありますが、beforeFind()でも同じことだと思われます。

対策

ビヘイビアのコードを追っていけば、もっといい方法はあるかもしれませんが、とりあえず以下の回避方法があります
対策1
beforeFind()で設定せずに、コントローラーで生成して直接conditionに突っ込む。
対策2
初期化時などにパラメータを事前に生成しておいてbeforeFind()ではセットするだけにする。

すでに開発が進んでいたため、多くの箇所を変更しなければならない対策1は却下。
ということで対策2を採用
ちょうど、ビヘイビアの初期化用の関数を読んでいた箇所があったため、そこでついでにfindを読んでconditionを生成。beforeFind()でセットするという方法をとりました。
これならbeforeFind()内でfindメソッドが呼ばれないので大丈夫でした。

バグなのか仕様なのか微妙なところですが、APIの仕組み上仕方がないかなとは思いますが、ちょっと根が深い問題でした。


コメントは受け付けていません。