CakePHPのPaginator
自分が忘れたときの為にメモ。
Controllerのpaginateを利用するとき、細かな条件は$this->paginateフィールドに定義してやるとOK。形式はfindに渡す定義と同じ。
CakeのModelのJOIN定義は、SELECT文発行するときにあまり柔軟に使えないのが難点ですが、このパラメータにjoinsという名前で定義してやれば、柔軟に選択することが出来るようになります。
$this->paginate['joins'] = array( array( 'type' => 'INNER', 'table' => 'users', 'alias' => 'User', 'conditions' => 'Product.user_id = User.id' ), array( 'type' => 'LEFT OUTER', 'table' => 'companies', 'alias' => 'Company', 'conditions' => 'Product.company_id = Company.id' ) );
こんな感じで。難点は、aliasやconditionsをいちいち書かなきゃいけないところでしょうか。せっかくModelに結合定義しているのだから、関連するModel名を書いておけば、ここら辺は省略可能とかできたらより便利になるんでしょうけど。
また、GROUP BY等を設定していると、ページングの総数を取得するSQLがおかしくなって正常な総数が取れなくなってしまいます。これは渡した条件のfields部分を、Cake側が単純にcount(*)に置き換えているのが原因です。本来なら、Hibernate等のように、作成されたSQL文全体を副問い合わせとして、その結果にたいするCOUNT(*)を取ってやるのが確実なんですけど、今のCakeの作りだとその方法が取り辛いです。なので、利用するModelにpaginateCountメソッドを定義して、そこで個別に対応することになります。前述したGROUP BYが原因の場合は、group定義をunsetして(参照渡ししないように注意)count(distinct )とか使ってデータ取得時と同じ件数になるように調整してみました。
後、ハマったのがViewで使うHelperの部分。何も考えずにpaging関連のHelperを使うと、FORMで定義したパラメータがpagingでは一つも送られないという酷い状態になります(苦笑)。ここでは、引数に
<?php echo $paginator->prev('<< '.__('previous', true), array('url' => $this->data['User}']), null, array('class'=>'disabled'));?> | <?php echo $paginator->numbers(array('url' => $this->data['User']));?> <?php echo $paginator->next(__('next', true).' >>', array('url' => $this->data['User']), null, array('class'=>'disabled'));?>
のようにパラメータを渡してやり、Controller側では
if (!isset($this->data['User']['name']) && isset($this->passedArgs['name'])) { $this->data['User']['name'] = $this->passedArgs['name']; } $this->User->set($this->data); if (!$this->User->validates()) { $this->render('index'); return; }
みたいな感じで、passedArgsからも値を取得出来るようにしておきます。Cakeのvalidateはinsert、update時にしか自動で呼ばれないので、SELECT文で使うときには個別に呼び出してやる必要があります。ここもちょっと面倒・・・