2024-07-04

Laravelのページネーションの仕組み

Laravelのページネーションのコードリーディングメモ。

$users = User::paginate(15);
<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>
 
{{ $users->links() }}

ページネーションデータの準備

Builder::paginate() で処理されます。

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null, $total = null)
{
    $page = $page ?: Paginator::resolveCurrentPage($pageName);

    $total = value($total) ?? $this->toBase()->getCountForPagination();

    $perPage = ($perPage instanceof Closure
        ? $perPage($total)
        : $perPage
    ) ?: $this->model->getPerPage();

    $results = $total
        ? $this->forPage($page, $perPage)->get($columns)
        : $this->model->newCollection();

    return $this->paginator($results, $total, $perPage, $page, [
        'path' => Paginator::resolveCurrentPath(),
        'pageName' => $pageName,
    ]);
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Eloquent/Builder.php#L920-L939

Paginator::resolveCurrentPage() ではPaginator::currentPageResolver() で設定したClosureを使ってページ番号を取得します。

Paginator::currentPageResolver(function ($pageName = 'page') use ($app) {
    $page = $app['request']->input($pageName);

    if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
        return (int) $page;
    }

    return 1;
});

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/PaginationState.php#L19-L27

page のリクエストパラメータから番号を取得、バリデーションした上で値を返しています。

Builder::getCountForPagination() ではモデルのカウントを取っています。 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Query/Builder.php#L3034-L3078

Builder::forPage() ではページ番号、1ページあたりのレコード数を使ってOFFSET/LIMITでレコードを取得します。

public function forPage($page, $perPage = 15)
{
    return $this->offset(($page - 1) * $perPage)->limit($perPage);
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Query/Builder.php#L2596-L2599

BuildsQueries::paginator() ではLengthAwarePaginatorクラスのインスタンスを作成します。

protected function paginator($items, $total, $perPage, $currentPage, $options)
{
    return Container::getInstance()->makeWith(LengthAwarePaginator::class, compact(
        'items', 'total', 'perPage', 'currentPage', 'options'
    ));
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Concerns/BuildsQueries.php#L492-L497

ページネーションの表示

LengthAwarePaginator::links()LengthAwarePaginator::render() を呼び出しています。

public function links($view = null, $data = [])
{
    return $this->render($view, $data);
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/LengthAwarePaginator.php#L77-L80

render() はビューをレンダリングしています。引数なしだと$defaultViewである pagination::tailwind をレンダリングします。

public function render($view = null, $data = [])
{
    return static::viewFactory()->make($view ?: static::$defaultView, array_merge($data, [
        'paginator' => $this,
        'elements' => $this->elements(),
    ]));
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/LengthAwarePaginator.php#L89-L95 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/AbstractPaginator.php#L117 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/resources/views/tailwind.blade.php

pagination:: のnamespaceとビューファイル定義は以下で行っています。

public function boot()
{
    $this->loadViewsFrom(__DIR__.'/resources/views', 'pagination');

    // ...(省略)...
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/PaginationServiceProvider.php#L16

simplePaginate()cursorPaginate()

ちなみに、 simplePaginate() の方を呼び出すと Paginator のインスタンスが返り、これの links() render() メソッドは pagination::simple-tailwind のビューをレンダリングしています。 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/Paginator.php#L94-L111 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Pagination/AbstractPaginator.php#L124

cursorPaginate()BuildsQueries::paginateUsingCursor() 経由で CursorPaginator のインスタンスを返しています。

public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
{
    $perPage = $perPage ?: $this->model->getPerPage();

    return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
}

https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Eloquent/Builder.php#L976-L981 https://github.com/laravel/framework/blob/v11.0.7/src/Illuminate/Database/Concerns/BuildsQueries.php#L371-L452

このエントリーをはてなブックマークに追加