ikasama over technology

忘れたくないことを忘れないために

CakePHP 2.x の PaginatorHelper をハックする

TL;DR

この CookBook にあるようなパラメータを $this->request->params['paging'] に渡すことで任意のページネーション表示ができます。

https://book.cakephp.org/2.0/ja/core-libraries/helpers/paginator.html#PaginatorHelper::params

実際にパラメータを渡すサンプル。

$params = [
    'page'      => 2,
    'current'   => 10,
    'count'     => 777,
    'prevPage'  => false,
    'nextPage'  => true,
    'pageCount' => 4,
    'order'     => null,
    'limit'     => 10,
    'options'   => [],
    'paramType' => 'querystring',
];

$this->request->params['paging']['YourModel'] = $params;

背景

いまどき CakePHP 2 かよ! とお思いかもしれませんが、なかなかレガシーから抜け出せない組織、ありますよね。 弊社ではようやく新しいフレームワークに移行することが決まったんですが、それがまさかの CakePHP 3 。 どうせならもっとほかのフレームワークにしてくれや……と内心思いながら移行の仕事をしています。

今回の移行は、利用者や役割の異なる複数のアプリケーションにそれぞれ書かれている、同じようなビジネスロジックAPI に一本化してしまおうという目的があります。 単なるデータフェッチなら API にまるっと移してほぼ同じ構造のデータを返してもらい、 Controller とかがそれを受け取ればいいだけです。 しかし Paginator は View に がっつり Helper として入っていて、正直修正したくない。 *1 ということで Paginator によるデータフェッチをレガシーから切り離しつつ、 レガシーの Paginator には出来合いのパラメータを渡して PaginatorHelper を生きながらえさせる作戦を考えました。

実装

PaginatorHelper のパラメータサンプルの URL を再掲します。

https://book.cakephp.org/2.0/ja/core-libraries/helpers/paginator.html#PaginatorHelper::params

各パラメータの意味はこんな感じ。間違っていたら指摘ください。

$params = [
    'page'      => 2,             // 現在のページ番号
    'current'   => 17,            // 現在のページに表示している件数
    'count'     => 3,             // 全体の件数
    'prevPage'  => false,         // 前のページがあるか?
    'nextPage'  => true,          // 次のページがあるか?
    'pageCount' => 4,             // 全体のページ数
    'order'     => null,          // ソート順
    'limit'     => 26,            // 1 ページの件数( {:start}, {:end} の計算に使うのはこちら )
    'options'   => [],            // ページネーションのオプション。なんかいろいろあるっぽい。
    'paramType' => 'querystring', // 生成するリンクのタイプ。この場合は ?page=2 のようなリンクを生成する。
];

こういうデータを渡してあげることで、実際のデータフェッチ内容とは無関係のページング UI を生成できます。

f:id:ikasamak503:20190309234948p:plain
{:end} がちゃんと計算されていてえらい

しかし無意味なページング UI を構築しては使い物にならないので、フェッチしたデータとの整合性をとる必要があります。 今回、データフェッチは API 化して HTTP の JSON でレスポンスを返してくれる実装になりました。 なので、データと横並びでページングのパラメータをセットし、それをそのまま横流しする形をとりました。 気を付ける点として、 API 側は CakePHP 3 のため、若干パラメータのキーが変わっています。 以下、 CakePHP2 => CakePHP 3 の形式です。

  • order => sort *2
  • limit => perPage

この変換は CakePHP 3 側に実装しました。 こういった互換性対応は新旧どちらに実装するかは好みによるところですが、新 : 旧 = 2 : 4 という事情もあって新側に実装しました。 *3

余談

今回の CakePHP 2 => CakePHP 3 移行でこういうのも作りました。 レガシーと戦う全国 5000 億人の CakePHPer のためにも、また別の機会に紹介できたらなと思います。

  • Paginator でフェッチしたときに明示的にページングパラメータをセットするのが面倒なので自動化
  • CakePHP 3 の Entity を CakePHP 2 の配列形式と型に変換

まとめ

  • $this->request->params['paging'] にパラメータを渡すことで PaginatorHelper を操れます
  • CakePHP 2 <=> CakePHP 3 の Paginator のパラメータには一部互換性がないので気を付けよう

参考

*1:もちろんユニットテスト、UI 自動テストなんで気の利いたものはありません

*2:今回は order を使っていないので、ここの互換性は確かめてないです

*3:いずれ捨てることになるので旧側に作ってそのまま捨てたいという気持ちもある