paginateで複雑な検索を行う

joinやサブクエリ、集合演算などを含む複雑な検索を行う場合、

  1. まず検索条件にマッチする全行のidを求め、
  2. Model.id IN (1で求めた全id) という条件で一覧表示に使うデータを取得する

という手順を踏んだ方が合理的なことがある。

この場合、1の段階で「検索条件にマッチする全行数」が判明するため、Paginationのために改めてCOUNTクエリを発行するのは完全に無駄である。CakePHP 1.2のPagination機能でこの無駄を回避するには、コントローラの $paginate プロパティに適当なキー(下例では numberOfRows)で件数を保存しておき、

$allIds = $this->Foo->find('all', array('fields' => 'DISTINCT Foo.id',
                                        'conditions' => $complexCond));
$this->paginate['numberOfRows'] = $this->Foo->getNumRows();

対象となるモデルの paginateCount メソッドでその数値をそのまま返すようにする。

class Foo extends Model
{
  function paginateCount($conditions=null, $recursive=0, $extra=array()) {
    if(isset($extra['numberOfRows'])) {
      return $extra['numberOfRows'];
    }
    $parameters = compact('conditions');
    if ($recursive != $this->recursive) {
      $parameters['recursive'] = $recursive;
    }
    return $this->find('count', array_merge($parameters, $extra));
  }
}

$paginate の中で認識されないキーは、Model::paginate や Model::paginateCount の最後の $extra 引数として渡ってくる。

Leave a Reply