例えば、User::paginate(5)
で返されるデータは、@foreachでループできるゆえにコレクションと思いきや、そうでありません。
ページネーション
ページネーションは、以下の画面のように複数のレコードをページごと閲覧できるために使われます。
上の表示に使われるコントローラは、以下のように簡単なコードです。
namespace App\Http\Controllers; use App\Models\User; class UserController extends Controller { public function index() { $users = User::paginate(2); //1ページ2個表示の設定 return view('users.index', compact('users')); } }
ブレードも簡単です。
@extends('layouts.app') @section('content') <div class="main py-4"> <div class="card card-body border-0 shadow table-wrapper table-responsive"> <h2 class="mb-4 h5">会員名</h2> <table class="table table-hover"> <thead> <tr> <th class="border-gray-200">名前</th> <th class="border-gray-200">メールアドレス</th> </tr> </thead> <tbody> @foreach ($users as $user) <tr> <td><span class="fw-normal">{{ $user->name }}</span></td> <td><span class="fw-normal">{{ $user->email }}</span></td> </tr> @endforeach </tbody> </table> <div class="card-footer px-3 border-0 d-flex flex-column flex-lg-row align-items-center justify-content-between"> {{ $users->links() }} </div> </div> </div> @endsection
表示するレコードを変える
さて、画面1で表示されるコードに会員IDを追加して、以下のように表示したいです。
ブレードを以下のようにすれば簡単に表示を変更できます。
... <td><span class="fw-normal">{{ $user->id }} : {{ $user->name }}</span></td> ...
しかし、ブレードを変更せずに、$user->name
に会員ID + 名前の値としたいなら、どうしましょうか?
先のコントローラの定義において、
... $users = User::paginate(2); $users->each(function($user) { $user->name = $user->id.' : '.$user->name; }); ...
と変更すればよいです。transform()
を使用してもよいです。
... $users = User::paginate(2); $users->transform(function($user) { $user->name = $user->id.' : '.$user->name; return $user; }); ...
され、これらをみて、誰しも思うのは、以下のようにメソッドをチェーンすることです。わかりやすいし、Laravelらしい。
... $users = User::paginate(2)->each(function($user) { $user->name = $user->id.' : '.$user->name; }); ...
しかし、そうするとエラーとなってしまいます。
そこで気が付くのは、User::paginate(2)
が返すのはCollectionではない、ということです。
通常は、以下のようにCollectionを返します。
>>> get_class(User::all()); => "Illuminate\Database\Eloquent\Collection"
しかし、ページネーションだと、
>>> get_class(User::paginate(2)); => "Illuminate\Pagination\LengthAwarePaginator"
返すのは、Collectionではなく、LengthAwarePaginatorです。これには、ページに表示するレコードだけでなく、何番目のページとか、他のページに移動するためのリンクとか、など他にもたくさんデータが詰まっています。
>>> User::paginate(2)->toArray(); [!] Aliasing 'User' to 'App\Models\User' for this Tinker session. => [ "current_page" => 1, "data" => [ [ "id" => 1, "name" => "村山 涼平", "email" => "kenichi.yoshida@example.org", "email_verified_at" => "2022-02-11T02:11:12.000000Z", "created_at" => "2022-02-11T02:11:12.000000Z", "updated_at" => "2022-02-11T02:11:12.000000Z", ], [ "id" => 2, "name" => "笹田 裕樹", "email" => "shuhei99@example.com", "email_verified_at" => "2022-02-11T02:11:12.000000Z", "created_at" => "2022-02-11T02:11:12.000000Z", "updated_at" => "2022-02-11T02:11:12.000000Z", ], ], "first_page_url" => "http://localhost?page=1", "from" => 1, "last_page" => 2, "last_page_url" => "http://localhost?page=2", "links" => [ [ "url" => null, "label" => "« Previous", "active" => false, ], [ "url" => "http://localhost?page=1", "label" => "1", "active" => true, ], [ "url" => "http://localhost?page=2", "label" => "2", "active" => false, ], [ "url" => "http://localhost?page=2", "label" => "Next »", "active" => false, ], ], "next_page_url" => "http://localhost?page=2", "path" => "http://localhost", "per_page" => 2, "prev_page_url" => null, "to" => 2, "total" => 3, ]
それゆえに、先ほどのようにメソッドのチェーンをしてデータの変更をすると、これらのデータが失われエラーとなってしまうのです。それでは、どのようにメソッドをチェーンできるのでしょう?
paginate()にチェーン
そのためのメソッドがあるのです。
... $users = User::paginate(2)->through(function($user) { $user->name = $user->id.' : '.$user->name; return $user; }); ...
through()
は、Collectionのtransform()
のようなもので、ページネーションのオブジェクトのdataの部分だけの編集が可能です。
ちなみに、以下のようにtap()
を使用しても同様なことができます。
(参照:https://stackoverflow.com/questions/37102841/laravel-change-pagination-data)
... $users = tap(User::paginate(2), function($page) { return $page->each( function($user) { $user->name = $user->id.' : '.$user->name; }); }); ...
なるほど、tap()
はこのようなときに使うのですね。paginate()
が返すLengthAwarePaginatorのオブジェクトはキープして、その中身を操作することが可能なのです。