Slim 3 で Eloquent の Query Builder を使って Pagination を実装する

Slim 3 で Pagination を実装するにあたり、ざっと探してみていいライブラリが見つからなかったのでメモしておきます。

前提として [Using Eloquent with Slim - Slim Framework](https://www.slimframework.com/docs/cookbook/database-eloquent.html) に従って Eloquent を導入した状態で進めていきます。

ポイントは

- Eloquent の Query Builder の count メソッドは便利

くらいでしょうか。

まずは PHP のコード。

```php
// ページ付き URL に強制転送
if (! array_key_exists('page', $args)) {
    header('Location: /page/1');
    exit;
}

// View に送るデータ
$values = [];

// クエリを配列として取得する
$params = $request->getQueryParams();

// クエリからフィルターに使う項目を取得し、DB の WHERE で結果を絞る
if (array_key_exists('sex', $params)) {
    $this->table->where('sex', '=', $params['sex']);
    $values['sex'] = $params['sex'];
}

// レコード数を取得する
// Eloquent の count メソッドは WHERE でレコードを絞った後のレコード数を取得できる
$numberOfItems = $this->table->count();

// 1ページあたりに表示するアイテム数
$itemsPerPage = 10;

// ページ数: アイテム数をページあたりのアイテム数で割って切り上げ
$numberOfPages = ceil($numberOfItems / $itemsPerPage);
$values['pages'] = $numberOfPages;

// take メソッドで LIMIT 指定
$this->table->take($itemsPerPage);

// offset メソッドで OFFSET 指定
$this->table->offset(($args['page'] - 1)* $itemsPerPage);

// レコードを取得して配列にする
$users = $this->table->get();
$values['users'] = $users;

// 現在のページを View に渡す
$values['page'] = $args['page'];

// フィルター用のクエリを取得し View で再利用
if ($query = $request->getUri()->getQuery()) {
    $values['query'] = '?' . $query;
}

$this->view->render($response, 'index.twig', $values);
return $response;
```

次に Twig のコード。

```php
<body>
    <div>
        <ul>
            {# 前のページへのリンク。現在のページが1のときは href を設定しない。 #}
            <li><a {{ page > 1 ? 'href=/page/' ~ (page - 1) : '' }}{{ query }}>&lt; Prev</a></li>
            {% for i in range(1, pages) %}
             <li><a href="/page/{{ i }}">{{ i }}</a></li>
            {% endfor %}

            {# 次のページへのリンク。現在のページが最後のページのときは href を設定しない。 #}
            <li><a {{ page < pages ? 'href=/page/' ~ (page + 1) : '' }}{{ query }}>Next &gt;</a></li>
        </ul>
    </div>
    <div>
        <table style="text-align:center">
            <thead>
                <tr>
                    <th style="width:4rem">ID</th>
                    <th style="width:18rem">NAME</th>
                    <th style="width:4rem">SEX</th>
                    <th style="width:12rem">BIRTH</th>
                </tr>
            </thead>
            <tbody>
            {% for user in users %}
                <tr>
                    <td>{{ user.id }}</td>
                    <td>{{ user.name }}</td>
                    <td>{{ ['male', 'female'][user.sex] }}</td>
                    <td>{{ user.birthday | date('Y/m/d') }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>

    <form method="get" action="/page/1">
        <label>Filter: Sex </label>
        <select name="sex">
            <option value="0" {{ sex == 0 ? 'selected' : '' }}>male</option>
            <option value="1" {{ sex == 1 ? 'selected' : '' }}>female</option>
        </select>
        <button type="submit">Submit</button>
    </form>
    <a href="/">Reset filter</a>
</body>
```

Pagination についてはライブラリがいろいろ見つかるのですが、ページの構成を作るのは View に任せたほうがいいのかなと思って Twig のテンプレートで作っています。

Eloquent の Query Builder は count メソッドが WHERE でレコードを絞った後のレコード数を数えてくれるのが便利ですね。

サブクエリとか意識せずに使えるのは助かります。

ちょっと大雑把な説明になりましたが、現場からは以上です。