cakePHPのfindAllで柔軟なJOINを行う

やりたいこと

あるコントローラでfindを実行する際に,一箇所でのみ特定のテーブルとJOINをしたり,状況によってJOIN設定を変更する方法について考える.


もっとも、そこまで特殊で数箇所でしか使用しないリレーションの場合,直接SQLを書いてしまったほうがラクだったりもする。
今回、findをJOINするのは,他のコンポーネントとの兼ね合い。特にPaginationコンポーネントを利用する場合に、直接SQLを書くとコンポーネントを利用したページング設定に手間がかかりそうだから。


$model->beforeFindを設定することで、そのmodelに関するfindのSQLを発行するときのJOINの設定などを行うことができるが、これはmodel自体の設定のため、findを使用している箇所全てに適用される。
(詳細は→CakePHP findAll で INNER JOIN する方法 | Sun Limited Mt. )
ということで今回、もう少し柔軟にJOINを発行する方法について考えてみる。

環境

php5 + cakephp 1.1.17.5612

とりあえずfindAllのコードを見てみる

cakephpの./cake/libs/model/model_php5.phpないのfindAllの部分を読んでみると..

  • $queryDataにSQLに関する情報が入っている。
  • $queryDataの'joins'にJOINの情報を入れてやればやればよさそう!
    • cakePHP自体のコード変更に抵抗がない場合はfindAllに引数$joinを追加してやるのが一番てっとり早そう
    • cakePHP自体のコード変更ができない場合は$model->beforeFindを使うしかない
    • 2つの場合、それぞれについてfind毎にJOINする方法を考える

cakePHP自体のコード変更する場合

  • ./cake/libs/model/model_php5.phpを以下のように変更
<?php
   //最終引数に$joinsを追加
   function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null,$joins = null) {

		//--------略-----------------

        $queryData = array('conditions' => $conditions,
                            'fields'    => $fields,
                            'joins'     => $joins, //変更部分
                            'limit'     => $limit,
                            'offset'    => $offset,
                            'order'     => $order
        );


		//--------略-----------------

        return $return;
    }
?>
  • コントローラー内での使い方は以下のようになる
<?php
    $joins[] = array(
        "type" => "INNER",
        "alias" => "",
        "table" => INNER JOIN するテーブル,
        "conditions" => INNER JOIN の ONに指定する条件,
    );

    $findAll($conditions , $fields, $order, $limit, $page, $recursive,$joins);
?>

beforeFindを用いる場合(cakePHP本体のコード変更しない場合)

  • cakePHP本体のコードが変更できない場合は$model->beforeFindを定義することになる
  • しかしbeforeFindはモデル自体に適応されるため、コントローラーすべてのfindに適用される
  • find毎に設定を変更するためにプロパティを追加する方法を考えてみる
./app/model/モデル名.phpを編集
  • プロパティにjoinSettings、メソッドにbeforeFindを以下のように追加
<?php
    var $joinSettings = array();

    function beforeFind(&$queryData){
            $queryData["joins"] = $this->joinSettings;
            return true;
        }
コントローラー側での使い方
  • JOINが必要ないときは通常にfindを使用
  • JOINが必要なときはfindの前に以下のように記述
<?php
        $this->モデル名->joinSettings[] = array(
                'type' => 'LEFT',
                'alias' => '',
                'table' => 'テーブル名',
                'conditions' => 条件,
            );

?>
  • これで特定の箇所だけ特定のJOINを実行できるはず