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文で使うときには個別に呼び出してやる必要があります。ここもちょっと面倒・・・