例えば、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のオブジェクトはキープして、その中身を操作することが可能なのです。



