Laravel5.4、検索機能付きペジネーション

タグ: Laravel5.4  

初心者の方もすぐにLaravelのペジネーションの簡単さ、便利さに気づかれるかと思います。

しかし、検索機能を付け加えるには、一段高いハードルが存在します。そこで、サンプルで学んでください。Laravelの認証で使用されるUserモデルを利用した、サンプルです。

真新しいLaravel環境の用意

既存の環境を壊してはいけませんので、新しくLaravelをインストールしましょう。ここでは簡単にセットアップを説明します。

まず、LaravelをComposerを使用しインストールします。現在、バージョン5.4が最新版ですので、以下のコンソールコマンドでは5.4がインストールされます。

composer create-project laravel/laravel インストール先ディレクトリ名

インストールが完了したら、インストール先のディレクトリへ移動してください。

続いて、.envファイルか、config/database.phpファイルにより、データベース環境を設定します。

Laravelの標準的な認証機能を使用するために、必要なファイルを生成します。

php artisan make:auth

続いて、データベーステーブルをマイグレーション機能で生成します。必要なマイグレーションは、この時点で準備されています。ただ、マイグレーションを実行します。

php artisan migrate

これで準備完了です。PHPの組み込みサーバーで起動し、最初のユーザーを作成してください。

php artisan serve

ブラウザから、http://127.0.0.1:8000/registerへアクセスすると、標準的な登録フォームが表示されます。

  • /register : ユーザ登録
  • /login : ログイン
  • /logout : ログアウト

ユーザーデータの生成

ペジネーションの動作を確認するためには、ある程度のユーザー数を登録し、usersテーブルのレコードを増やしておく必要があります。手動で行うには煩雑ですので、Laravelのシーディング機能、データベースの初期値設定機能を使います。

今回はdatabase/seeds/DatabaseSeeder.phpを直接変更します。以下のように更新してください。

public function run()
{
    // $this->call(UsersTableSeeder::class);
    factory(App\User::class, 20)->create();
}

factoryメソッドで、サンプルデータを生成可能です。20は件数です。20件生成しています。実際に生成しましょう。

まだ、PHPの組み込みサーバーが動作している状態でしたら、CTRL+Cで停止させてください。コンソールから、次のコマンドでシーディングが実行されます。

php artisan db:seed

もし、最初に手動で1件登録していれば、合計で21件分のデータが存在していることになります。

ビューの準備

resouces/views/userlist.blade.phpファイルを作成します。検索のために名前とメールアドレスを指定する入力フォームを付けた、ペジネーションビューです。Laravelの認証機能で標準的に生成したビューに合わせてあります。これらはBootstrap CSSフレームワークを使用していますが、きれいに表示するためにdivタグを付けすぎると見づらくなるため、見た目にはこだわらず省略しています。

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">ユーザーリスト</div>
                <div class="panel-body">
                    <p>一致数:{{ $users->total() }}</p>
                    <form>
                        <input type="text" name="name" value="{{ $name }}" placeholder="登録名">
                        <input type="text" name="email" value="{{ $email }}" placeholder="メールアドレス">
                        <input type="submit" value="submit">
                    </form>
                    <table>
                        <thead>
                            <tr>
                                <td>ID</td>
                                <td>登録名</td>
                                <td>メールアドレス</td>
                            </tr>
                        </thead>
                        @foreach ($users as $user)
                        <tr>
                            <td>{{ $user->id }}</td>
                            <td>{{ $user->name }}</td>
                            <td>{{ $user->email }}</td>
                        </tr>
                        @endforeach
                    </table>
                    {{ $users->links() }}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

検索情報を指定するフォームと、ペジネーション結果を表示するテーブルです。LaravelのBladeテンプレートを使用しています。

<form>で送信メソッドやアクションを指定していないのは間違いでありません。LaravelのペジネーションはGETメソッドでのページ付けを想定していますので、フォームもデフォルトのGETメソッドで送信します。

$users変数の中身は、User Eloquentインスタンスではなく、それを含んだペジネーションインスタンスです。ドキュメントの最後に使用できるメソッドが記述されています。totalメソッドは、usersテーブルに対して行われたクエリーに一致したレコード数を表します。

最後の{{ $users->links() }}は、ページ操作を行うボタン(リンク)を表示するメソッドです。

ロジック部

これはサンプルコードの提示を目的とした記事です。手数を減らすためにコントローラを使用せず、ルートに直接ロジックを記述します。

まず、検索機能を付けない場合の標準的なロジックを確認しましょう。

use Illuminate\Http\Request;
use App\User;

Route::get('/paginate', function (Request $request) {

    $pagedUsers = User::paginate(10);

    return view('userlist')
        ->with('users', $pagedUsers);
});

Userモデルに対し、paginateメソッドで「何件ごとにページ分けするか」を指定するだけです。とても簡単です。

ここに、指定されたUserモデルの検索条件を使い、クエリーするようにします。

use Illuminate\Http\Request;
use App\User;

Route::get('/paginate', function (Request $request) {
    $user = new User();

    if ($request->has('email')) {
        $user = $user
            ->orWhere('email', 'like', '%'.$request->input('email').'%');
    }

    if ($request->has('name')) {
        $user = $user
            ->orWhere('name', 'like', '%'.$request->input('name').'%');
    }

    $pagedUsers = $user->paginate(10)
    ->appends($request->only(['name', 'email']));

    return view('userlist')
        ->with('name', $request->input('name'))
        ->with('email', $request->input('email'))
        ->with('users', $pagedUsers);
});

Userモデルはクエリービルダーのようにも動作します。クエリービルダーとはその名の通り、クエリーを組み立てるための便利機能です。今回は、条件が指定されていることをリクエストインスタンスのhasメソッドでチェックし、ビルダーのorWhereメソッドで条件をUserインスタンスに付け加えています。

orWhereですので、各検索条件はORで結ばれます。どれか一つにでも合致されれば表示されます。AND条件にしたければ、whereメソッドを使用します。

条件が用意できた時点で、paginateメソッドでペジネーターインスタンスを取得します。このメソッドだけでもページ付けは行えますが、ページを切り替えるとフォーム入力の内容がURLに含まれないために、条件がクリアされてしまいます。

そこで、ペジネータークラスのappendsメソッドへURLへ含める検索条件を配列で渡します。「検索条件」と記述したのは、配列の内容が変更されると、表示されるページが1ページになります。条件が異なれば、全体のページ数もかわります。そこで、通常1ページへ戻す処理が必要となりますが、既に組み込まれていますので、appendsメソッドに検索条件を渡すだけで、あとはLaravelに任せられます。

リクエストインスタンスに対するonlyメソッドは、入力フィールドの名前を指定し、そのキー名と値のペアの配列を生成してくれます。今回の場合、次のような配列が生成され、appendsメソッドに渡されます。

[
    ['name' => nameフィールドの入力値],
    ['email' => emailフィールドの入力値]
]

再度、PHPの組み込みサーバーを動作させ、http://127.0.0.1:8000/paginateにアクセスし、動作を確認してください。

最後に

実は、クエリービルダー、ファサードではDB::で使用されるインスタンスは次のように使用できます。

$user = DB::table('users');
$user->orWhere(...); // 条件の付加
$user->orWhere(...); // 条件の付加
$user->get(); // 実際のクエリー発行、結果取得

今回直接確認していませんが、Eloquentモデルでも次のようにできます。

$user = (new User)->where(...);
$user->orWhere(...); // 条件の付加
$user->orWhere(...); // 条件の付加
$user->get(); // 実際のクエリー発行、結果取得

一度クエリーの条件を指定したインスタンスであれば、自身や別のインスタンスに代入せずとも条件を加えられます。

今回のコードは次のようにしています。

$user = new User; // インスタンス化のみ
$user = $user->orWhere(...); // 条件の付加(実際はifブロックの中)
$user = $user->orWhere(...); // 条件の付加(実際はifブロックの中)
$user->get(); // 実際のクエリー発行、結果取得

最初のインスタンス化時に同時に指定できる条件がない場合があります。条件をEloquentモデルに付け加えている文はifで判定され、実行されない場合もあります。すると、どれが「最初」の条件付けになるかわからないため、全部$user自身に代入しています。

これを普通のクエリービルダーのように書いてしまうと、エラーにはなりませんが、条件がクエリーに反映されずに悩むことになります。

$user = new User; // インスタンス化のみ
$user->orWhere(...); // 条件の付加
$user->orWhere(...); // 条件の付加
$user->get(); // 実際のクエリー発行、結果取得

本来は、Eloquentモデルもクエリービルダーとして動作するとドキュメントで明記している以上、同じ動作をするのがわかりやすいと思いますが、はまりがちなポイントです。