ページ送り
開発環境:CakePHP2.5.1
GoogleやYahooなどの検索結果のページでよく見る1ページ目、2ページ目、前のページヘ、次のページヘなどのページ送り。
これを素のPHPで書こうとすると結構面倒ですが、CakePHPのPaginatorコンポーネントおよびPaginatorヘルパーを使えば簡単に書くことができます。
コントローラーでの準備
まずは、コントローラーでPaginatorコンポーネントを指定し、ページ送りのデフォルト条件を指定します。
<?php public $components = array('Paginator'); public $paginate = array( 'limit' => 10, 'order' => array( 'Post.id' => 'desc' ) ); ?>
そして、ページ送りを使うアクション内で、設定をセットし、PaginatorコンポーネントのpaginateメソッドにModelを指定してデータを取得します。
<?php public function index() { $this->Paginator->settings = $this->paginate; $data = $this->Paginator->paginate('Post'); $this->set(compact('data')); } ?>
ビューの書き方
ビュー側のPaginatorヘルパーは様々なメソッドが用意されています。
1|2|3|などのページ番号のリンク
<?php echo $this->Paginator->numbers(); ?>
前へ・次へのリンク
<?php echo $this->Paginator->prev('前へ'); ?> <?php echo $this->Paginator->next('次へ'); ?>
ページネーションの効率的な書き方
上記のようなコードをビューにそのまま書いても問題ないのですが、ページ送りの機能を他のページでも使う場合はエレメントにまとめてしまうとメンテナンスがラクになり、コードがすっきりします。
例えば、app/View/Elements/paginator.ctpというファイルを作り、その中に上記のコードを書いておきます。
それをビュー側から次のように呼び出せば、ビューにそのまま書くのと同じ状態が作れます。
<?php echo $this->element('paginator'); ?>
検索条件を引き継いだページ送り
検索結果のgetパラメータをページ送りで引き継ぐ方法です。
これは特に何も設定しなくてもCakePHP側が自動でそのようにやってくれます。
唯一行うのは、ビュー側の検索フォームをgetで渡すことくらいです。
<?php echo $this->Form->create('Post', array('url' => '/', 'type' => 'get')); echo $this->Form->input('title'); echo $this->Form->end('Search'); ?>
こうすると、ページ送りのURLは /page:2?title=hoge のようになります。
ページ送りを?page=2のようにする方法
デフォルトの状態だとページ数の部分は/page:2のような感じになっていますが、これを?page=2のようなパラメータで渡したい場合はコントローラーの設定で次のようにします。
<?php public $paginate = array( 'paramType' => 'querystring', );
paramTypeにquerystringを設定することで期待通りの形式のURLが生成されます。
ページ送りで重複データを省く
ページネーションで重複データを省くにはちょっと注意が必要です。
ポイントはDISTINCTを利用するのではなく、GROUP BYを利用することです。
詳しくは「ページネーションではDISTINCTではなく、GROUP BYを使う」をご覧ください。
自動でどんどん読み込むページ送り
FacebookやTwitterなどでよく見るスクロールしていくと次から次に表示されるタイプのもの。
あれを実現するにはJQueryを使うことで対応できます。
インフィニティスクロールを使った無限スクロールのやり方はこちらからご覧ください。
paginateのパラメータをモデルに書いて効率的に管理
最初に紹介したようにコントローラーにpaginateパラメータを設定するのが一般的ですが、一つのコントローラーに複数のページ送り機能があったりするときなど、ちょっと面倒になるのに加え、コントローラーのコード量も増えていきます。
それらを防ぐためにもpaginateのパラメータをモデルに書いてしまうのもおすすめです。
モデル側にはコントローラーで書いていた時と同様の書き方で、
<?php class Sample extends AppModel { public $paginate = array( 'conditions' => array( 'Sample.flg' => 0, ), 'limit' => 30, 'order' => array( 'Sample.id' => 'desc' ) ); }
このように書いておきます。
一方、コントローラー側には、
<?php App::uses('AppController', 'Controller'); class SampleController extends AppController { public $uses = array('Sample'); public $components = array('Paginator'); public $paginate; public function index() { $this->paginate = $this->Sample->paginate; $this->Paginator->settings = $this->paginate; $samples = $this->Paginator->paginate('Sample'); }
このようにモデル側のpaginateパラメータを呼び出して、本来のpaginatorにセットすれば完成です。