Laravel 6で一覧ページに検索窓とページ行数指定を付ける

　Laravel 6でindex()コントローラとビュー(Blade)を使ってレコード一覧を作るときに、Laravel標準ページネーションを使いつつ、キーワードによる絞り込み、1ページの表示数を指定できるようにする方法を紹介します。

　指定した条件は、URLのクエリーパラメータに以下のような形で明示されるようにします。これにより、ユーザーがリンクをほかのユーザーに共有した時に、そのユーザーも同じ画面が見られます。

```
/customers?keyword=a&perpage=10&page=3
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315589/7a93c2fe-7d95-3817-1dd6-e5e849d324c5.png)

今回は``Customer``モデルの一覧画面という設定で見ていきましょう。

# コントローラ

まずはコントローラ。

```php:CustomerController.php
    public function index(Request $request)
    {
        $query = Customer::query();

        $keyword = $request->input('keyword');
        if (!empty($keyword)) {
            $query->where('name', 'like', '%' . $keyword . '%');
        }

        $perpage = $request->input('perpage', 10);

        //paginate
        $customers = $query->paginate($perpage);
        return view('customers.index', ['customers' => $customers->appends($request->except('page')), 'request'=>$request->except('page')]);
    }
```

順に見ていきます。

検索キーワードと表示行数の入力をコントローラで受け取るため、``Request``オブジェクトをパラメータに追加します。

```php
public function index(Request $request)
```

次に検索条件を追加できるように``Customer``モデルからクエリービルダーを取得します。

```php
        $query = Customer::query();
```

検索キーワードをGetパラメータから取得し、指定があれば``where``句を追加します。

```php
        $keyword = $request->input('keyword');
        if (!empty($keyword)) {
            $query->where('name', 'like', '%' . $keyword . '%');
        }
```

また、表示行数もGetパラメータから取得し、指定があればその数値、なければ10行とします。
``Request``オブジェクトの``input``メソッドに第2パラメータを指定すると、規定値として処理してくれます。

```php
        $perpage = $request->input('perpage', 10);
```

指定条件で絞って、さらにページ処理(Pagination)を施した結果の``Customer``一覧を取得します。

```php
        $customers = $query->paginate($perpage);
```

最後に``customers.index``という``blade``(ビュー)に、絞り込んだ$customersを渡します。

```php
        return view('customers.index', ['customers' => $customers->appends($request->except('page')), 'request'=>$request->except('page')]);
```

このとき、``customer``のPaginationがpage以外のGetパラメータを適切に取り扱えるよう、以下のようにパラメータを渡しています。

```php
'customers' => $customers->appends($request->except('page'))
```

さらにbladeに、手動でGetパラメータ全体(Page以外)を渡しています。

```php
'request'=>$request->except('page')]
```

#　ビュー

まずPaginationリンク。これは標準のまま。

```php:index.blade.php
{{ $customers->links() }}
```

次に検索ボックス。

```php:index.blade.php
<form method="get">
    @foreach ($request as $key=>$value)
        @if ($key!="keyword")
            <input type="hidden" name="{{$key}}" value="{{$value}}" />
        @endif
    @endforeach
    <input type="text" class="form-control" name="keyword" placeholder="Name" value="{{$request["keyword"] ?? ""}}">
</form>
```

自分のパラメータである``keyword``以外については``hidden``で保持します。ただし``page``はコントローラ側で排除したので、ここに保持されません。つまり、キーワード検索を実施すると必ず1ページ目に戻ることになります。
さらに、``input``ボックスの値は``{{$request["keyword"] ?? ""}}``とし、キーワードが既に指定されていればそれ、なければ空文字列とします。なお、``??``はPHP7以降で導入されたnull合体演算子という便利な三項演算子です。

同じように1ページの行数指定ドロップダウンを作ります。

```php:index.blade.php
<form method="get">
    @foreach ($request as $key=>$value)
        @if ($key!="perpage")
            <input type="hidden" name="{{$key}}" value="{{$value}}" />
        @endif
    @endforeach
    <select class="custom-select" name="perpage" onchange="this.form.submit()">
        <option value="10" {{($request["perpage"] ?? "")==10?"selected":""}}>10</option>
        <option value="20" {{($request["perpage"] ?? "")==20?"selected":""}}>20</option>
        <option value="50" {{($request["perpage"] ?? "")==50?"selected":""}}>50</option>
        <option value="100" {{($request["perpage"] ?? "")==100?"selected":""}}>100</option>
    </select>
</form>
```

形は違いますがやってることは上と同じです。
本来なら行数指定がまだの場合、デフォルトの選択が何になるか明示すべきですが、デフォルトの選択肢が一番上に来ているため、初期状態の"selected"指定はなし＝一番上になる、ということにして手抜きしています。

# なぜ``customers``に格納されているパラメータを使わないのか

　コントローラからビューに渡す``$customers``には、実はクエリーパラメータが格納されています。しかし、このパラメータは仕様上``protected``になっていて、blade内で読むことはできません。そのため、自前で$requestをbladeに渡しているのです。