CakePHPで高速Webアプリ開発

第15回Paginationで面倒なページ繰り処理とおさらばしよう

「Paginationのために(CakePHP 1.1から)1.2にアップグレードしたい」と言う人がいるほど、Paginationは便利なものです。今回はPaginationの紹介と、サンプルコードを3つ紹介いたします。

Webアプリに欠かせない「ページ繰り」は自前でやるのは面倒!

ページ繰りとは、⁠次のページ」「前のページ」の基本的なリンクから、⁠1 2 3 4 5 …」といったページ番号でのリンクなどのことです図1⁠。

図1 一般的なページ繰り表示
図1 一般的なページ繰り表示

複数のデータを列挙するWebアプリケーションでは、必ずと言っていいほどこのページ繰りが必要になります。

しかし、ページ繰りをすべて自前でやるのは意外に面倒で、加減算や除算を駆使した計算をずらずら書かないといけません。表示のときにも場合わけが必要で、1ページ目なら「前へ」はリンクしませんし、最後のページなら「次へ」はリンクしません。

「次へ」⁠前へ」だけならまだしも、ページ番号の列挙などになると、計算に加えて「最大で5件表示するけど5件に満たないとき」などの例外への対応も必要になります。さまざまなパターンの動作確認のためのデータの用意も手間ですね。

このようにすべてのケースに対応できるような汎用的なコードを書こうとすると結構大変なので、仕様を簡略化したり、場当たり的に行っていた場合も少なくないのではないでしょうか。

そんな面倒なページ繰りを、計算から表示までほぼ全て引き受けてくれるのが「Pagination」機構なのです。

Pagination例その1:基本のサンプルコード

もしPaginationのコードを見るのが初めてな場合、⁠えっ、これだけでいいの?」と驚くことでしょう。

Paginationはコントローラーでセットアップし、ビューで表示させます。まずはコントローラです。

リスト app/controller/sample_controller.php
<?php
class SampleController extends AppController {
  var $name = 'Sample';
  var $uses = array('Post');
  var $paginate = array(
    'limit' => 25,
    'order' => array(
      'Post.id' => 'desc'
    ),
  );
  functon posts() {
    $this->set('posts', $this->paginate('Post'));
  }
}

次にビューです。PaginatorHelperで表示します。PaginatorHelperはコントローラーで指定しなくても読み込まれています。

リスト app/viess/sample/posts.ctp

<?php
  echo $paginator->prev('前へ');
  echo $paginator->numbers();
  echo $paginator->next('次へ');
?>

たったこれだけで、前後のリンクとページ番号の列挙が完成しました。たとえば1ページ目を表示しようとして、最大3ページの場合、以下のようなHTMLが出力されます。

| <span class="current">1</span> | <span><a href="/sample/posts/page:2">2</a></span> | <span><a href="/sample/posts/page:3">3</a></span> | <a href="/sample/posts/page:2">次へ</a>

ブラウザ上の表示は図2のようになります。

図2 サンプルの出力結果
図2 サンプルの出力結果

「前へ」の表示がありませんが、1ページ目なので自動的に表示しないように処理されています。1ページ目でも何か表示したければprevメソッドの第三引数に文字列を渡せば1ページ目のときのみ出力されます。リンクは行われません。

$paginator->prev('前へ', null, '前へ')

また、ページ番号の列挙がspanで囲われていますが、これもnumbersメソッドへの引数によってliなどの要素にすることもできますし、適切にCSSをあてれば図1のように表示させることも可能です。

Pagination例その2:検索条件や取得フィールドなどを指定する

取得するレコードの検索条件の指定や、取得するフィールドの絞り込み、recusive値などを指定することもできます。

<?php
class SampleController extends AppController {
  var $name = 'Sample';
  var $uses = array('Post');
  var $paginate = array(
    'conditions' => array(
      'Post.status' => 'active',
    ),
    'fields' => 'Post.id, Post.title, Post.created',
    'order' => 'Post.id desc',
    'recursive' => -1,
  );
  functon posts() {
    $this->set('posts', $this->paginate('Post'));
  }
}

パラメータの形式はModel::find()メソッドの引数と同じです。

Pagination例その3:URLからのパラメータも引き継ぐ

URLから受け取ったパラメータを引き継ぎたいときは、ひと手間必要です。

例は、カテゴリーIdをURLから受け取れるようにして、http://example/sample/posts/123といったURLでアクセスする場合です。引き継ぎたい値をsetメソッドでビューに渡します。

<?php
class SampleController extends AppController {
  var $name = 'Sample';
  var $uses = array('Post');

  functon posts($category_id) {
    $this->paginate = array(
      'conditions' => array(
        'Post.category_id' => $category,
      ),
      'order' => 'Post.id desc',
    );
    $this->set('posts', $this->paginate('Post'));
    $this->set('paginator_option', array('url' => array($category_id)));
  }

ビュー側では、prev,next,numbersなどのメソッドの引数に引き継ぎたいパラメータを渡します。

<?php
  echo $paginator->prev('前へ', $paginator_option));
  echo $paginator->numbers($paginator_option);
  echo $paginator->next('次へ', $paginator_option);
?>

出力結果は以下のようになります。

| <span class="current">1</span> | <span><a href="/sample/posts/123/page:2">2</a></span> | <span><a href="/sample/posts/123/page:3">3</a></span> | <a href="/sample/posts/123/page:2">次へ</a>

また、⁠array($category_id)」の部分を「array('category_id' => $category_id)」とすれば、URLは「/sample/posts/category_id:123/page:2」になります。全てのURLに対応するにはルーティングの設定が必要になることがありますが、不可能ではありません。ちょっと複雑ですが、Paginationの恩恵は大きいので、ぜひマスターしたいところです。

まだまだある、PaginatorHelperのメソッド群

Paginatorができるのは前後のリンクとページ番号だけではありません。PaginatorHelperのメソッド群を利用すればかなり複雑なページ繰り表示にも自由に対応できます。メソッド群の一部を表にしました。

counter()「1ページ目を表示 4ページ中」といった表示
current()現在のページ番号
first()最初のページへのリンク
last()最後のページへのリンク
hasNext()次のページがあるか (true or false)
hasPrev()前のページがあるか (true or false)
hasPage($page)指定のページ番号のページがあるか (true or false)
$paramsページ数などの情報やパラメータが詰まった連想配列。例えば $paginator->params['paging']['Post']['count'] で件数を取得できる。

とくに$paginator->params変数の中身は、自由なページ繰り表示には欠かせませんので、var_dumpするなどして一度構造を確認しておくと良いでしょう。

おすすめ記事

記事・ニュース一覧