LaravelとReactでページネーションを実装する
LaravelとReactを使ってページネーション機能を初めて実装したのでメモしておきます。
Laravelページネーションの仕様確認
ページネーションの一番簡単な実装方法は、クエリビルダーまたは Eloquentのpaginate
メソッドを使うことです。
// クエリビルダーを使う場合
use Illuminate\Support\Facades\DB;
$users = DB::table('users')->paginate(15)
// Eloquentを使う場合
use App\Models\User;
$users = User::paginate(15);
また、メソッドは下記の通り3種類ありますが、今回はpaginate
メソッドを使っていきます。
メソッド | 返されるインスタンス |
---|---|
paginate |
Illuminate\Pagination\LengthAwarePaginator |
simplePaginate |
Illuminate\Pagination\Paginator |
cursorPaginate |
Illuminate\Pagination\CursorPaginator |
返される値を確認
$usersPaginator = User::paginate(5);
dd($usersPaginator);
Illuminate\Pagination\LengthAwarePaginator {
#items: Illuminate\Database\Eloquent\Collection {#1353 ▶}
#perPage: 5
#currentPage: 1
#path: "http://localhost:8000/users"
#query: []
#fragment: null
#pageName: "page"
+onEachSide: 3
#options: array:2 [▶]
#total: 111
#lastPage: 23
}
自動でJSON形式に変換される
paginator
インスタンスは、ルーティングやコントローラーからreturnするだけでJSON形式に自動的に変換されます。(paginator
クラスがIlluminate\Contracts\Support\Jsonable
を実装するため)
{
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"first_page_url": "http://laravel.app?page=1",
"last_page_url": "http://laravel.app?page=4",
"next_page_url": "http://laravel.app?page=2",
"prev_page_url": null,
"path": "http://laravel.app",
"from": 1,
"to": 15,
"data":[
{
// Record...
},
{
// Record...
}
]
}
引用: Database: Pagination - Laravel 10.x - The PHP Framework For Web Artisans
(ドキュメントには書いてありませんでしたが、上記に加えてlinks
という配列も含まれます。)
Laravel側の実装
さて、上述の通りLaravelからページネーションに必要なデータがJSON形式で返ってくることがわかったので、Reactへpropsで渡せば自由に使えそうです。
usersPaginator
という名前で上記JSONオブジェクトをReactに渡すこととします。
User一覧ページに表示することにします。
// routes/web.php
Route::get('users', [UserController::class, 'index'])->name('users.index');
コントローラの実装。ここではInertiaを使っています。
// app/Http/Controllers/UserController.php
use App\Models\User;
use Inertia\Inertia;
public function index()
{
$usersPaginator = User::paginate(20);
return Inertia::render('User/Index', [
'usersPaginator' => $usersPaginator,
]);
}
Reactにページネーション実装
Paginationコンポーネント作成
親コンポーネントからprops
として渡されるpaginator
オブジェクトから、必要な情報を分割代入を使って変数として取り出します。
// resources/js/Components/Pagination.jsx
export default function Pagination({ paginator }) {
const {
first_page_url,
last_page_url,
next_page_url,
prev_page_url,
current_page,
last_page
} = paginator;
return (
<div className="pagination">
<a
href={first_page_url}
className="pagination-link"
aria-disabled={current_page === 1}
>
最初
</a>
<a
href={prev_page_url}
className="pagination-link"
aria-disabled={!prev_page_url}
>
前へ
</a>
<span className="current-page">
{current_page} / {last_page}
</span>
<a
href={next_page_url}
className="pagination-link"
aria-disabled={!next_page_url}
>
次へ
</a>
<a
href={last_page_url}
className="pagination-link"
aria-disabled={current_page === last_page}
>
最後
</a>
</div>
);
}
first_page_url
などは該当のURLを持っているため、a
タグのリンク先に指定するだけです。
// ページ遷移ボタン例
<a
href={first_page_url}
className="pagination-link"
aria-disabled={current_page === 1}
>
最初
</a>
aria-disabled
属性にtrue
/false
が入るように条件式を書いておき、true
のとき使用不可のスタイリングを書いておきます。
aria-disabled={current_page === 1}
aria-disabled={!prev_page_url}
aria-disabled={!next_page_url}
aria-disabled={current_page === last_page}
// pagination.scss
.pagination-link {
&[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}
}
親コンポーネント作成
親コンポーネントのUser/Index.jsx
の適当な場所にPagination
コンポーネントを埋め込みます。
// resources/js/Pages/User/Index.jsx
import Pagination from "@/Components/Pagination";
export default function Index({ usersPaginator }) {
return (
<>
<Pagination paginator={usersPaginator} />
...
</>
);
}
件数を表示したい場合はtotal
を使います。
{usersPaginator.total}件
usersPaginator.data
はUserオブジェクトを格納したコレクション(配列)を持っているため、map
を使って回せば一覧表示もできます。
<ul>
{usersPaginator.data.map(user => (
<li key={user.id}>
{user.id}: {user.name}
</li>
))}
</ul>
補足: Sassレイアウト
上記でコーディングしたページネーションコンポーネントのClassNameに対応したSassも適当に書いたので一応載せときます。
.pagination {
align-items: center;
display: flex;
justify-content: center;
}
.pagination-link {
background-color: #eee;
border-radius: 4px;
border: 1px solid #ddd;
color: #333;
padding: 4px 8px;
transition: background-color 0.2s ease;
&+.pagination-link {
margin-left: 4px;
}
&[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}
&:hover {
background-color: #ddd;
}
}
.current-page {
font-size: 0.9rem;
margin: 0 4px;
}
以上で簡単ではありますがシンプルなページネーションが実装できました。Laravel返されるページネーションのJSONオブジェクトをReactで取り扱うだけなので、Laravel側のページネーションの仕様がわかっていれば簡単に実装できそうです。