CakePHPのpaginateにURLパラメータを渡す方法

akiyanさんのすばらしい資料を参考にさせていただいていたのだけれど、若干不満があって調べてみた。

スライド28ページ

$this->paginate = array('Model' => array(
  'options' => array(
    '?' => array('keyword' => 'fuga'),
    '#' => 'results',
  )
);

というコードなのだけれど、問題は、2ページ目なんかを表示した時に、

Notice (8): Undefined index:  limit [CORE/cake/libs/view/helpers/paginator.php, line 414]

というのが発生することだった。これはこれでその場しのぎで回避する方法もあるんだけど調べてみたところ、

cake/libs/controller/controller.php:1041

あたりの

        extract($options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options));

というコードを利用しているようだ。この$optionsのところさっきの$this->paginateに与えたのが渡ってくる。

んで、ここでarray_mergeした結果を$optionsに入れなおすんだけど、実はこのときextractする直前では$optionsの中が

  array(
    'page' => 1,
    'limit' => 20,
    'options' => array(
      '?' => array('keyword' => 'fuga'),
      '#' => 'results',
    )
  )

ってなことになっていて、それをextractすると$page,$limit,$optionsという変数が定義されることになる。つまり、$optionsという変数は上書きされちゃって、

  array(
    '?' => array('keyword' => 'fuga'),
    '#' => 'results',
  )

になっちゃうわけだ。こういうところでextractを使うCakePHPのセンスもどうかと思うが、こんな荒技っつーか裏技にakiyanさんはどうやって気づいたんだろう。

で、僕の代替案は、view側で$paginator->options()を使う方法。通常$paginatorを使う時は、

$paginator->options(array('url' => $this->passedArgs));

するとイイヨーとドキュメントにも書いてあるけど、ここで'?'を使って

$paginator->options(array('url' => am(
    $this->passedArgs,
    array('?' => array('keyword' => 'fuga'))
)));

とする。もっとありがちなコードとして、受け取ったパラメータをそのまま引き継ぐには、

$url = $this->passedArgs;
$params = am(array('keyword' => ''), $this->params['url']);
$url['?']['keyword'] = $params['keyword'];
$paginator->options(array('url', $url));

とか。あんまり綺麗じゃないですが、この手のことをする場合はだいたいこのkeywordを入力するinputを、

echo $form->input('keyword', array('value' => $this->params['url']['keyword']));

とvalueを引き継ぐ必要があるけどここでもUndefined indexを回避したいので、上記のようにしておいて、

echo $form->input('keyword', array('value' => $params['keyword']));

とするのが良いと思います。