LoginSignup
8
5

More than 3 years have passed since last update.

Laravelでページネーション

Last updated at Posted at 2020-07-05

目次

Laravelの記事一覧は下記
PHPフレームワークLaravelの使い方

Laravelバージョン

動作確認はLaravel Framework 7.19.1で行っています

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Laravelでデータベースを操作する(クエリビルダ編)
本記事は上記が完了している前提で書かれています
本記事は上記で作成したフォルダとファイルを使用します

サービスクラス修正

ここで修正するサービスクラスは
Laravelでデータベースを操作する(クエリビルダ編)
で作成したものです
(1) /sample/app/Services/Interfaces/Table2Service.php修正
selectメソッドの引数修正
引数に$limit = null, $page = null, $sort = null追加

Table2Service.php
‥‥
    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col, $limit = null, $page = null, $sort = null);
‥‥

(2) /sample/tests/Services/Impl/Table2ServiceImpl.php修正
selectメソッドの引数修正
引数に$limit = null, $page = null, $sort = null追加

Tests\Services\Impl\Table2ServiceImpl.php
‥‥
    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col, $limit = null, $page = null, $sort = null)
    {
    }
‥‥

(3) /sample/app/Services/Impl/Table2ServiceImpl.php修正
selectメソッド修正

App\Services\Impl\Table2ServiceImpl.php
‥‥
    public function select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col, $limit = null, $page = null, $sort = null)
    {

        $columns = [
            'table1.id            as table1_id',
            'table1.varchar_col   as table1_varchar_col',
            'table1.int_col       as table1_int_col',
            'table1.datetime_col  as table1_datetime_col',
            'table1.date_col      as table1_date_col',
            'table1.time_col      as table1_time_col',
            'table2.id            as table2_id',
            'table2.varchar_col   as table2_varchar_col',
            'table2.int_col       as table2_int_col',
            'table2.datetime_col  as table2_datetime_col',
            'table2.date_col      as table2_date_col',
            'table2.time_col      as table2_time_col',
            'table3.id            as table3_id',
            'table3.varchar_col   as table3_varchar_col',
            'table3.int_col       as table3_int_col',
            'table3.datetime_col  as table3_datetime_col',
            'table3.date_col      as table3_date_col',
            'table3.time_col      as table3_time_col',
        ];

        $db = DB::table('table2')
                    ->leftJoin('table1', 'table2.table1_id', '=', 'table1.id')
                    ->leftJoin('table3', 'table2.id', '=', 'table3.table2_id')
                    ->select($columns);

        if (!is_null($id)) {
            $db->where('table2.id', '=', $id);
        }
        if (!is_null($varchar_col)) {
            $db->where('table2.varchar_col', 'like', '%'. addcslashes($varchar_col, '\\_%') . '%');
        }
        if (!is_null($int_col)) {
            $db->where('table2.int_col', '=', $int_col);
        }
        if (!is_null($datetime_col)) {
            $db->where('table2.datetime_col', '=', $datetime_col);
        }
        if (!is_null($date_col)) {
            $db->where('table2.date_col', '=', $date_col);
        }
        if (!is_null($time_col)) {
            $db->where('table2.time_col', '=', $time_col);
        }

        if (!is_null($sort)) {
            $sortCol = ['table2.id',
                        'table2.varchar_col',
                        'table2.int_col',
                        'table2.datetime_col',
                        'table2.date_col',
                        'table2.time_col'];
            $db->orderBy($sortCol[$sort]);
        }

        if (!is_null($page) && !is_null($limit)) {
            $recordList = $db->paginate($limit, $columns, 'page', $page);
        } else {
            $recordList = $db->get();
        }

        return $recordList;

    }
‥‥

大きな修正は$db->paginateメソッドを追加したことです
paginateメソッドの
第1引数は1ページに表示する件数です。
第2引数はselectするカラムです。
今回は
DB::table('table2')
‥‥
->select($columns);
で取得するカラムを指定しているので意味ないです
第3引数はページ番号が送信されてくるHTTPリクエストパラメーター名です。
第4引数はページ番号です。select文ではこの引数-1がoffsetになります
第2引数から第4引数は省略できます。省略した場合、第2引数は[*]、第3引数は'page'、第4引数はnullになり、ページ番号は第3引数名のHTTPパラメーター値になります(第3引数、第4引数を省略すれば、pageという名前のHTTPパラメーター値となる)
今回はクエリビルダを使いましたが、paginateメソッドはEloquentでも使えます
Laravelでデータベースを操作する(Eloquent編)
でEloquentを使うサービスクラスを作成しました。そこではgetメソッドをつかってselectしましたが、getメソッドの部分を今回と同じpaginateメソッドに変更すれば、Eloquentでページネーションできます。クエリビルダのpaginateメソッドもEloquentのpaginateメソッドも引数は同じです

フォームリクエストの修正

ここで修正するフォームリクエストは
Laravelでデータベースを操作する(クエリビルダ編)
で作成したものです
/sample/app/Http/Requests/Table2Request.php修正

Table2Request.php
‥‥
    public function rules()
    {
‥‥
        return [
            'id'               => [$idRequire, 'numeric', 'max:18446744073709551615'],
            'table1_id'        => ['nullable', 'numeric', 'max:18446744073709551615'],
            'varchar_col'      => ['nullable', 'max:255'],
            'int_col'          => ['nullable', 'integer', 'max:2147483647'],
            'datetime_col'     => ['nullable', 'date_format:Y-m-d H:i:s'],
            'date_col'         => ['nullable', 'date_format:Y-m-d'],
            'time_col'         => ['nullable', 'date_format:H:i:s'],
            'limit'            => ['nullable', 'integer', 'min:1', 'max:200'],
            'page'             => ['nullable', 'integer', 'min:1', 'max:2147483647'],
            'sort'             => ['nullable', 'integer', 'min:0', 'max:5'],
        ];
    }
‥‥

limit、page、sortの入力制限を追加しました

Controllerにメソッド追加

(1) /sample/app/Http/Controllers/SampleController.phpにpaginateQueryBuilderメソッドを追記
フォームリクエスト、サービスクラスのuse文は
Laravelでデータベースを操作する(クエリビルダ編)
で書きました

SampleController.php
    public function paginateQueryBuilder(Table2Request $request, Table2Service $table2Service)
    {
        if (is_null($request->session()->get('errors'))) {
            $request->flash();
        }

        $id = $request->input('id');
        $varchar_col = $request->input('varchar_col');
        $int_col =  $request->input('int_col');
        $datetime_col =  $request->input('datetime_col');
        $date_col =  $request->input('date_col');
        $time_col =  $request->input('time_col');
        $limit =  $request->input('limit') ?? 5;
        $page =  $request->input('page') ?? 1;
        $sort =  $request->input('sort') ?? 0;

        $recordList = null;
        if (is_null($request->session()->get('errors'))) {
            $recordList = $table2Service->select($id, $varchar_col, $int_col, $datetime_col, $date_col, $time_col, $limit, $page, $sort);
        }

        $data = [
            'recordList'   => $recordList,
            'id'           => $id,
            'varchar_col'  => $varchar_col,
            'int_col'      => $int_col,
            'datetime_col' => $datetime_col,
            'date_col'     => $date_col,
            'time_col'     => $time_col,
            'limit'        => $limit,
            'page'         => $page,
            'sort'         => $sort
        ];

        return view('sample.paginate', $data);
    }

(2) /sample/routes/web.phpに下記を追記
Route::get('sample/paginate-query-builder', 'SampleController@paginateQueryBuilder');

viewの作成

(1) ページネーションビューの作成
ページネーションビューとは前のページへリンク、次のページへリンク、ページ番号リンクを描画するビューです
コマンドラインで
cd sample
php artisan vendor:publish --tag=laravel-pagination
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します
eclipseプロジェクトを右クリック→リフレッシュ
resources/views/vendor/paginationフォルダが現れます。そのフォルダの中に5つファイルができています
その中のbootstrap-4.blade.phpが今回使うページネーションビューになります。
ページネーションビューを独自のデザインにする場合、bootstrap-4.blade.phpを修正してください
今回修正したサービスクラスで使ったpaginateメソッドの戻り値(LengthAwarePaginatorクラスのインスタンス)がデフォルトで使用するページネーションビューがpagination::bootstrap-4です。
LengthAwarePaginator::defaultViewメソッドでデフォルトビューを変えることができます。また、デフォルトビューを変えなくても、後程使うlinksメソッドの第1引数にビュー名を渡すことでもカスタムページネーションビューを使用できます

(2) /sample/resources/views/sample/paginate.blade.phpファイル作成

paginate.blade.php
<html>
    <head>
        <title>sample</title>
        <style>
        .sample-table {
            border-collapse:collapse;
            white-space: nowrap;
            border: 1px solid #000000;
        }
        .sample-table thead {
            background-color: #33CCFF;
            color: #FFFFFF;
            font-weight: bold;
        }
        .sample-table td {
            padding-left:10px;
            padding-right:10px;
            border: 1px solid #000000;
        }
        .pagination li {
            display: inline-block;
        }
        </style>
    </head>
    <body>

        <form action="{{ url('sample/paginate-query-builder') }}" method="get">
            @error('id')
                @foreach ($errors->get('id') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>id<input type="text" name="id" value="{{ old('id') }}"></div>
            @error('varchar_col')
                @foreach ($errors->get('varchar_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>varchar_col<input type="text" name="varchar_col" value="{{ old('varchar_col') }}"></div>
            @error('int_col')
                @foreach ($errors->get('int_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>int_col<input type="text" name="int_col" value="{{ old('int_col') }}"></div>
            @error('datetime_col')
                @foreach ($errors->get('datetime_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>datetime_col<input type="text" name="datetime_col" value="{{ old('datetime_col') }}"></div>
            @error('date_col')
                @foreach ($errors->get('date_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>date_col<input type="text" name="date_col" value="{{ old('date_col') }}"></div>
            @error('time_col')
                @foreach ($errors->get('time_col') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>time_col<input type="text" name="time_col" value="{{ old('time_col') }}"></div>
            @error('limit')
                @foreach ($errors->get('limit') as $error)
                    <div style="color:red;">{{ $error }}</div>
                @endforeach
            @enderror
            <div>1ページ表示件数<input type="text" name="limit" value="{{ old('limit', 5) }}"></div>
            <input type="submit" >
        </form>

        <br>

        @isset($recordList)
            @if ($recordList->total() === 0)
                <div>
                    検索結果は0件です
                </div>
            @else
                <table class="sample-table">
                    <thead>
                        <tr>
                            <td colspan=6>table1</td>
                            <td colspan=6>table2</td>
                            <td colspan=6>table3</td>
                        </tr>
                        <tr>
                            <td>id</td>
                            <td>varchar_col</td>
                            <td>int_col</td>
                            <td>datetime_col</td>
                            <td>date_col</td>
                            <td>time_col</td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 0])
                                                       ->url($recordList->currentPage()) }}" >id</a></td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 1])
                                                       ->url($recordList->currentPage()) }}" >varchar_col</a></td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 2])
                                                       ->url($recordList->currentPage()) }}" >int_col</a></td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 3])
                                                       ->url($recordList->currentPage()) }}" >datetime_col</a></td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 4])
                                                       ->url($recordList->currentPage()) }}" >date_col</a></td>
                            <td><a href="{{ $recordList->appends([
                                                                    'id'           => $id,
                                                                    'varchar_col'  => $varchar_col,
                                                                    'int_col'      => $int_col,
                                                                    'datetime_col' => $datetime_col,
                                                                    'date_col'     => $date_col,
                                                                    'time_col'     => $time_col,
                                                                    'limit'        => $limit,
                                                                    'page'         => $page,
                                                                    'sort'         => 5])
                                                       ->url($recordList->currentPage()) }}" >time_col</a></td>
                            <td>id</td>
                            <td>varchar_col</td>
                            <td>int_col</td>
                            <td>datetime_col</td>
                            <td>date_col</td>
                            <td>time_col</td>
                        </tr>
                    </thead>
                    <tbody>
                            @foreach ($recordList as $record)
                                <tr>
                                    <td>{{ $record->table1_id }}</td>
                                    <td>{{ $record->table1_varchar_col }}</td>
                                    <td>{{ $record->table1_int_col }}</td>
                                    <td>{{ $record->table1_datetime_col }}</td>
                                    <td>{{ $record->table1_date_col }}</td>
                                    <td>{{ $record->table1_time_col }}</td>
                                    <td>{{ $record->table2_id }}</td>
                                    <td>{{ $record->table2_varchar_col }}</td>
                                    <td>{{ $record->table2_int_col }}</td>
                                    <td>{{ $record->table2_datetime_col }}</td>
                                    <td>{{ $record->table2_date_col }}</td>
                                    <td>{{ $record->table2_time_col }}</td>
                                    <td>{{ $record->table3_id }}</td>
                                    <td>{{ $record->table3_varchar_col }}</td>
                                    <td>{{ $record->table3_int_col }}</td>
                                    <td>{{ $record->table3_datetime_col }}</td>
                                    <td>{{ $record->table3_date_col }}</td>
                                    <td>{{ $record->table3_time_col }}</td>
                                </tr>
                            @endforeach
                    </tbody>
                </table>
            @endif
        @endisset

        @isset($recordList)
            {{ $recordList->appends([
                                    'id'           => $id,
                                    'varchar_col'  => $varchar_col,
                                    'int_col'      => $int_col,
                                    'datetime_col' => $datetime_col,
                                    'date_col'     => $date_col,
                                    'time_col'     => $time_col,
                                    'limit'        => $limit,
                                    'page'         => $page,
                                    'sort'         => $sort
                                    ])
                          ->links() }}
        @endisset

    </body>
</html>

thead->tr->tdタグ内のaタグのhref属性では$recordList->appends()->url()を実行してます。$recordListはLengthAwarePaginatorクラスのインスタンスです。
$recordList->currentPage()は現在のページ番号を返します
url($recordList->currentPage())は$recordList->currentPage()ページのURLを返します。
$recordList->appendsはurl()が返すURLに付加するクエリ文字列です
よく見ていただくと、appendsメソッドに渡している配列のsort要素の値を変えています

paginate.blade.php下部に書いた$recordList->appends()->links()は先ほど生成したbootstrap-4.blade.phpを描画します
linksメソッドの第1引数にビュー名を渡すことでbootstrap-4.blade.phpではないカスタムページネーションビューを描画することもできます
appendsメソッドは先ほど説明した通りページリンクURLに付加するクエリ文字列です

動作確認

http://localhost/laravelSample/sample/paginate-query-builder
にアクセスすると画面下部にページリンクが描画されています

POSTに対応してみる

今作成したものはGETメソッドに対応したものでした。
多くの場合、検索はGETメソッドで大丈夫でしょうが、
検索条件がたくさん設置され、長い文字列カラムが検索条件に入っている場合など、GETメソッドにした場合、URLの文字列制限に引っかかり、URLのクエリ文字列が途中で切れてしまうこともあります
そのような事情で検索をPOSTメソッドで実装した場合に対応してみましょう

(1) まず、resources/views/sample/paginate.blade.phpのformタグのmethod属性値をpostに変え、sample/routes/web.phpに定義した'sample/paginate-query-builder'のルーティングをRoute::getからRoute::match(['get', 'post']に変更します(今回は動作確認でsample/paginate-query-builderに直リンクするためgetも許容しておく)

(2) resources/views/vendor/pagination/bootstrap-4.blade.php修正
@if ($paginator->hasPages())直下(2行目)に下記を追記

    @php
        $___paginatorHiddenFunc = function ($name, $value) use (&$___paginatorHiddenFunc) {
            if (is_array($value)) {
                foreach ($value as $k => $v) {
                    $___paginatorHiddenFunc($name . '[' . $k . ']', $v);
                }
            } else {
                echo '<input type="hidden" name="' . htmlspecialchars($name, ENT_QUOTES) . '" value="' . htmlspecialchars($value, ENT_QUOTES) . '">';
            }
        };
        $___paginatorQueryList = null;
        parse_str(parse_url($paginator->url(1), PHP_URL_QUERY), $___paginatorQueryList);
    @endphp

やっていることは2つです
type属性値がhiddenのinputタグを出力するメソッドを定義している($___paginatorHiddenFunc)
1ページ目のページリンクのクエリ文字列を配列として取得している($___paginatorQueryList)

bootstrap-4.blade.phpにはaタグが3つ書いてあります
前のページへリンク、ページ番号リンク、次のページへリンクです
これらをformタグに直します

前のページへリンク

<form action="{{ $paginator->path() }}" method="post">
    @csrf
    @foreach ($___paginatorQueryList as $___paginatorQueryName => $___paginatorQueryValue)
        @if ($___paginatorQueryName === $paginator->getPageName())
            @php $___paginatorHiddenFunc($___paginatorQueryName, $paginator->currentPage() - 1); @endphp
        @elseif ($___paginatorQueryName !== '_token')
            @php $___paginatorHiddenFunc($___paginatorQueryName, $___paginatorQueryValue); @endphp
        @endif
    @endforeach
    <input type="submit" value="&lsaquo;">
</form>

ページ番号リンク

<form action="{{ $paginator->path() }}" method="post">
    @csrf
    @foreach ($___paginatorQueryList as $___paginatorQueryName => $___paginatorQueryValue)
        @if ($___paginatorQueryName === $paginator->getPageName())
            @php $___paginatorHiddenFunc($___paginatorQueryName, $page); @endphp
        @elseif ($___paginatorQueryName !== '_token')
            @php $___paginatorHiddenFunc($___paginatorQueryName, $___paginatorQueryValue); @endphp
        @endif
    @endforeach
    <input type="submit" value="{{$page}}">
</form>

次のページへリンク

<form action="{{ $paginator->path() }}" method="post">
    @csrf
    @foreach ($___paginatorQueryList as $___paginatorQueryName => $___paginatorQueryValue)
        @if ($___paginatorQueryName === $paginator->getPageName())
            @php $___paginatorHiddenFunc($___paginatorQueryName, $paginator->currentPage() + 1); @endphp
        @elseif ($___paginatorQueryName !== '_token')
            @php $___paginatorHiddenFunc($___paginatorQueryName, $___paginatorQueryValue); @endphp
        @endif
    @endforeach
    <input type="submit" value="&rsaquo;">
</form>

やっていることは
配列として取得した1ページ目のページリンクのクエリ文字列($___paginatorQueryList)をループし
type属性値がhiddenのinputタグを出力するメソッド($___paginatorHiddenFunc)を実行しているだけです

(3) resources/views/sample/paginate.blade.phpファイル作成
thead->tr->tdタグ内のaタグをformタグに変更します

<form action="{{ $recordList->path() }}" method="post">
 @csrf
 <input type="hidden" name="id" value="{{ $id }}">
 <input type="hidden" name="varchar_col" value="{{ $varchar_col }}">
 <input type="hidden" name="int_col" value="{{ $int_col }}">
 <input type="hidden" name="datetime_col" value="{{ $datetime_col }}">
 <input type="hidden" name="date_col" value="{{ $date_col }}">
 <input type="hidden" name="time_col" value="{{ $time_col }}">
 <input type="hidden" name="limit" value="{{ $limit }}">
 <input type="hidden" name="sort" value="0">{{-- ここは各カラムの番号にする --}}
 <input type="hidden" name="page" value="{{ $page }}">
 <input type="submit" value="id">{{-- ここは各カラム名にする --}}
</form>

動作確認

http://localhost/laravelSample/sample/paginate-query-builder
にアクセスします
ページリンクをクリックするとPOST送信できました
見た目は自由にCSSで変えてください

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5