Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Pagination

A thousand posts cannot go on one page. The list is split into pages, and Laravel counts the records, takes the right portion and draws the links by itself.

paginate

Call paginate() with the number of records per page instead of get():

public function index()
{
    $posts = Post::latest()->paginate(15);
 
    return view('posts.index', ['posts' => $posts]);
}

Laravel takes the page number from the address: /posts?page=2.

Printing in a template

The result is looped over like a regular collection, and the links() method prints the page links:

@foreach ($posts as $post)
    <article>
        <h2>{{ $post->title }}</h2>
    </article>
@endforeach
 
{{ $posts->links() }}

Information about the pages

<p>
    Showing records {{ $posts->firstItem() }}–{{ $posts->lastItem() }}
    of {{ $posts->total() }}
</p>
MethodWhat it returns
total()The total number of records
currentPage()The current page number
lastPage()The last page number
hasMorePages()Whether there are more pages

Keeping filters

If the list is filtered, the filter parameters must stay in the page links. withQueryString() does that:

$posts = Post::query()
    ->when($request->category, fn ($query, $category) => $query->where('category_id', $category))
    ->paginate(15)
    ->withQueryString();

The link to the second page becomes /posts?category=3&page=2.

Simple pagination

paginate() runs two queries: one for the records and one for their total. On large tables the count is slow. simplePaginate() shows only "Previous" and "Next" and needs one query:

$posts = Post::latest()->simplePaginate(15);

For an endless feed use cursorPaginate() — it is fast at any depth.

By default the links are marked up for Tailwind CSS. To change the markup, publish the templates into the project:

php artisan vendor:publish --tag=laravel-pagination

They appear in resources/views/vendor/pagination.

Pagination in an API

If the result of paginate() is returned from an API controller, it becomes JSON with the records and the page information:

{
    "data": [{ "id": 1, "title": "First post" }],
    "current_page": 1,
    "last_page": 7,
    "per_page": 15,
    "total": 100
}

Processing a large table

To go through every record in a background job, read them in chunks:

Post::where('is_published', true)->chunkById(500, function ($posts) {
    foreach ($posts as $post) {
        // processing
    }
});

Common mistake

Pagination without sorting gives unpredictable results: the database is not obliged to return rows in the same order, and a record may land on two pages or on none. Always set an order — latest(), orderBy('id').

Thanks for reading!

Donate to the site — we work hard for you.