Cakephpで同一ページで複数のページネーションを複数行なう
ウェブネーション、ページネーション、、
SGKです。
cakephpのページネーションって便利ですよね。
ところがこの前、1つのページで2つのページングを設置する必要があり、あれ、どーするんだろう?と詰まってしまいました。
ググっても解決できなかった点があったのでブログに記しておこうと思います。
目次
・まず通常1つのページングを表示する場合のController、Templateはこんな感じです。
// Controller: 有効なユーザーの一覧を10件づつ表示
$queryEnableUsers = $this->Users->find()->where([ 'enabled' => true ]);
$pageEnableUser = $this->Paginator->paginate( $queryEnableUsers, [ 'limit' => 10 ] );
$this->set( 'Users', $pageEnableUser );
※paginateプロパティを使う方法が一般的ですが、後の説明のため「Paginator を直接使用する方法」(→Coockbook)で進めます。
<!-- Template:ユーザー名の一覧とページ番号のリンクをを表示、例: 1 2 3 4 5 -->
<?php foreach($Users as $user ){ ?><p><?= $user->name ?></p><?php } ?>
<div><?= $this->Paginator->numbers() ?></div>
実に簡単に書けます!
・しかしページネーションを2つにするとなった場合、ある疑問が起こります。
controllerで以下のようにもう1つページネーションを追加した場合、
// Controller: ユーザーとアイテムを10件づつ表示
//ユーザー
$queryEnableUsers = $this->Users->find()->where([ 'enabled' => true ]);
$pageEnableUser = $this->Paginator->paginate( $queryEnableUsers, [ 'limit' => 10 ] );
$this->set( 'Users', $pageEnableUser );
//アイテム
$queryItems = $this->Items->find();
$pageItems = $this->Paginator->paginate( $queryItems, [ 'limit' => 10 ] );
$this->set( 'Items', $pageItems );
Templateで以下のようにするとどちらのページネーションがレンダリングされるのでしょうか?
<!-- Template -->
<div><?= $this->Paginator->numbers() ?></div>
どっちかのクエリのページングになるのですが、、どちらにせよ目的は達成できないので確認していません。。
・ではどうすれば良いか?
Templateでページングを分けるにはどうすれば良いか?、答えは Controller で scope をセットして Template で model で指定する、です。
// Controller: ユーザーとアイテムを10件づつ表示 paginate のオプションに scope をセット
//ユーザー
$queryEnableUsers = $this->Users->find()->where([ 'enabled' => true ]);
$pageEnableUser = $this->Paginator->paginate( $queryEnableUsers, [ 'limit' => 10, 'scope' => 'Users' ] );
$this->set( 'Users', $pageEnableUser );
//アイテム
$queryItems = $this->Items->find();
$pageItems = $this->Paginator->paginate( $queryItems, [ 'limit' => 10, 'scope' => 'Items' ] );
$this->set( 'Items', $pageItems );
<!-- Template:numbers のオプションに model をセット -->
<div><?= $this->Paginator->numbers(['model'=>'Users']) ?></div>
<div><?= $this->Paginator->numbers(['model'=>'Items']) ?></div>
上記でセットした scope、model は paginate でセットしたクエリのモデルのエイリアス名に合わせます。
・最後に、同じモデルのクエリの場合はどうするか?
scope、model は paginate でセットしたクエリのモデルのエイリアス名に合わせればよいので setAlias でエイリアス名を変えれば同じモデルのクエリを複数ページングする事ができます。
// Controller: 有効なユーザーと無効なユーザーを10件表示
//エイリアスを後で戻すため変更前のエイリアスを取得しておく
$tmpAlias = $this->Users->getAlias();
//有効なユーザー
$queryEnableUsers = $this->Users->setAlias('EnableUsers')->find()->where([ 'enabled' => true ]);
$pageEnableUsers = $this->Paginator->paginate( $queryEnableUsers, [ 'limit' => 10, 'scope' => 'EnableUsers' ] );
$this->set( 'pageEnableUsers', $pageEnableUsers );
//無効なユーザー
$queryDisableUsers = $this->Users->setAlias('DisableUsers')->find()->where([ 'enabled' => false ]);
$pageDisableUsers = $this->Paginator->paginate( $queryDisableUsers, [ 'limit' => 10, 'scope' => 'DisableUsers' ] );
$this->set( 'pageDisableUsers', $pageDisableUsers );
//エイリアス名を元に戻す
$this->Users->setAlias($tmpAlias);
ポイントは最初の getAlias と最後の setAlias です。
こうしてエイリアス名を戻しておかないと、次にこのモデルに対してfindを行う場合、エイリアス名が変わっていてはまるかもしれないのでやっておいた方が良いです。
<!-- Template -->
<div><?= $this->Paginator->numbers(['model'=>'EnableUsers']) ?></div>
<div><?= $this->Paginator->numbers(['model'=>'DisableUsers']) ?></div>
※上記のテクニックはわかりにくいけど実は Coockbook にも書いてある。
Controller側 Paginatorコンポーネント:→複数のクエリのページ分割
Template側 Paginatorヘルパー:→複数の結果の改ページ
同じページで2つのページングを表示できるようになったのですが、しかし不愉快です。
なぜ scope でセットした文字列を model で対応させずにエイリアスを参照するんでしょうか?
そしてなぜ Coockbook はバージョン4になっても読みにくいのでしょうか!!
以上 Cakephp で 同一ページで複数のページネーションを行なうテクニックでした!