Chapter 06

ttskch/paginator-bundleで一覧画面にページネーションを導入

たつきち
たつきち
2022.07.30に更新

この章に対応するコミット

デモアプリは日本語と英語に対応するためすべての文字列リテラルを翻訳しているので、コミットの内容は本文の解説と若干異なります。

ttskch/paginator-bundleで一覧画面にページネーションを導入

ユーザーのCRUDができたところで、一覧画面にページネーションを導入します。

ページネーションの実装には拙作の ttskch/paginator-bundle を使います。

手前味噌ですが、

  • 超軽量
  • Symfony以外の依存なし
  • Doctrine ORMと併用するためのヘルパー完備
  • ページャーのテンプレートはTwigを書いてカスタマイズ可能
  • ソートリンクのHTML出力機能あり
  • 検索フォームとの連携も簡単
  • Bootstrap4ベースの美しいページャーテンプレートをプリセット

と、正直言って至高のページネーションバンドルだと思ってます😂

ぜひ皆さんも使ってみてください🙏

細かな使い方については以下の過去記事で詳しく解説しているので、あわせてご参照ください。

[Symfony] シンプルでカスタマイズしやすい最強のページネーションバンドル

インストール

まずはインストールします。

$ composer require ttskch/paginator-bundle
// config/bundles.php

return [
    // ...
    Ttskch\PaginatorBundle\TtskchPaginatorBundle::class => ['all' => true],
];

設定

インストールしたら、バンドルの設定ファイルを作ります。

config/packages/ttskch_paginator.yaml に以下のような設定を書きます。(細かいところはお好みで)

ttskch_paginator:
  page:
    name: page
    range: 5
  limit:
    name: limit
    default: 50
  sort:
    key:
      name: sort
    direction:
      name: direction

      # "asc" or "desc"
      default: desc
  template:
    pager: '@TtskchPaginator/pager/bootstrap4.html.twig'
    sortable: '@TtskchPaginator/sortable/default.html.twig'

プリセットのBootstrap4テンプレートを利用するようにするのを忘れないようにしましょう。

コントローラ

続いて、コントローラの index アクションを修正します。

+ use Ttskch\PaginatorBundle\Context;
+ use Ttskch\PaginatorBundle\Doctrine\Counter;
+ use Ttskch\PaginatorBundle\Doctrine\Slicer;
  
  // ...
  
  /**
   * @Route("/", name="index", methods={"GET"})
   */
- public function index()
+ public function index(Context $context)
  {
+     $qb = $this->repository->createQueryBuilder('u');
+     $context->initialize('id', new Slicer($qb), new Counter($qb));
+ 
      return $this->render('user/index.html.twig', [
-         'users' => $this->repository->findAll(),
+         'slice' => $context->slice,
      ]);
  }

Ttskch\PaginatorBundle\Context をDIで受け取って、 $context->initialize() することによって内部的に handleRequest() が行われ、URLパラメータで指定されたページネーションが実行されます。

$context->initialize() の引数は、

  1. デフォルトのソート対象プロパティ名
  2. データ全体からページを切り出すためのスライス処理を行うためのcallable
  3. データ全体の件数をカウントするためのcallable

となっていて、2と3については、今回のようにDoctrine ORMと併用する場合にはバンドルにプリセットされている Ttskch\PaginatorBundle\Doctrine\SlicerTtskch\PaginatorBundle\Doctrine\Counter というヘルパークラス( __invoke() が実装されていてcallable)を利用することができます👍

ビュー

次に、ビューを修正して

  • テーブルの項目名をクリックしてソートの切り替えをできるように
  • 画面内にページャーを設置

します。

  {# ... #}
  
  <div class="table-responsive">
    <table class="table table-sm table-hover">
      <thead>
      <tr>
-       <th>ID</th>
-       <th>メールアドレス</th>
-       <th>アクセス権限</th>
-       <th>表示名</th>
-       <th>最終ログイン日時</th>
+       <th>{{ ttskch_paginator_sortable('id', 'ID') }}</th>
+       <th>{{ ttskch_paginator_sortable('email', 'メールアドレス') }}</th>
+       <th>{{ ttskch_paginator_sortable('roles', 'アクセス権限') }}</th>
+       <th>{{ ttskch_paginator_sortable('displayName', '表示名') }}</th>
+       <th>{{ ttskch_paginator_sortable('lastLoggedInAt', '最終ログイン日時') }}</th>
        <th></th>
      </tr>
      </thead>
      <tbody>
-     {% for user in users %}
+     {% for user in slice %}
        <tr>
          {# ... #}
        </tr>
        {# ... #}
      </tbody>
    </table>
  </div>
+ 
+ {{ ttskch_paginator_pager() }}

バンドルが提供してくれるTwig関数 ttskch_paginator_sortable()ttskch_paginator_pager() を呼び出すだけです。簡単ですね!

ソート状態を表すキャレットをCSSで表示

ttskch_paginator_sortable() で項目名を出力すると、その項目がソート対象となっている場合に自動で

<span class="desc">
  <a href="/user/?page=1&sort=id&direction=asc">ID</a>
</span>

のように asc または desc というクラスをつけて出力してくれます。

このままだと画面の見た目は変わらないので、この asc desc クラスに対してCSSで上下方向のキャレットを出力するようにします。

assets/scss/app.scss

span.asc:after {
  content: '\f0d8'; // fa-caret-up
  font-family: "Font Awesome 5 Free";
}
span.desc:after {
  content: '\f0d7'; // fa-caret-down
  font-family: "Font Awesome 5 Free";
}

を追記して、

$ yarn dev
# or
$ npm run dev

すれば適用されます。

フロントのアセットを編集する度に毎回ビルドするのが面倒であれば、常に

$ yarn watch
# or
$ npm run watch

しておくこともできます✋

動作確認

これで、下図のような感じで一覧画面にページネーションが導入できました!🙌