Monday, 21st September 2020

Custom paginators in Laravel

Let's assume we have some users in the database, or you can create some like so (this could go in a seeder):

User::factory(200)->create();

In Laravel, you can return paginated data to your views or API. You just need to call paginate on your model like so:

Route::get('users', function () {
    $users = User::paginate();

    return view('users')->with(compact('users'));
});

Then in your blade view you can use the data as normal, however you will now have access to a links() method that you can use in your view:

<ul class="ml-4">
    @foreach($users as $user)
        <li class="list-disc">{{ $user->name }}</li>
    @endforeach
</ul>

{{ $users->links() }}

This will generate the pagination links, styled for Tailwind CSS, however you can also configure it for bootstrap.

Image of Pagination

This is great however, we may have some data not in the database/eloquent so let's see how we can achieve the same result with a custom paginator.

Custom Paginators

Okay, first we need some data this should mimic what we had for the database data, so we should only need to make a minor change our blade template:

$data = \Illuminate\Support\Collection::range(1, 200)->map(function ($i) {
    return [
        'name' => "Person #{$i}",
    ];
});

We need know what page we are on, and get a subset of the data for that page, by default the pagination class assumes we will use a page item in the query string so let's follow that convention.

$page = request()->get('page', 1);
$users = $data->forPage($page, 15);

Now we can create our paginator, we need to give it the actual data, the total number of items, and how many items per page. We also need to tell it the path, so the links generated are on the correct page otherwise it will default to the domain root.

use Illuminate\Pagination\LengthAwarePaginator;

$paginator = new LengthAwarePaginator(
    $users,
    $data->count(),
    15
);

$paginator->setPath(request()->path());

We can now pass this to the view like so:

return view('users')->with(['users' => $users, 'paginator' => $paginator]);

and update the view like so:

<ul class="ml-4">
    @foreach($users as $user)
        <li class="list-disc">{{ $user['name'] }}</li>
    @endforeach
</ul>

{{ $paginator->links() }}

I like to pass through $users as an additional property as I think it makes the blade code more readable however, you can call the items() method on the paginator to get the data like so:

<ul class="ml-4">
    @foreach($paginator->items() as $user)
        <li class="list-disc">{{ $user['name'] }}</li>
    @endforeach
</ul>

{{ $paginator->links() }}

You will have something that looks like this:

Image of Pagination

I hope this helps, and you can find the official docs here.

Tweet me with any questions.

© 2020 Ashley Clarke. All rights reserved.