Manual and custom pagination in Laravel (without Eloquent)

- 👤 Andrés Cruz

🇪🇸 En español

Manual and custom pagination in Laravel (without Eloquent)

Many times we need to have some organization or grouping on some set of elements, whether it comes from the database or for example, data coming from an API using the Laravel HTTP Client, and by organization I mean being able to display a set of these elements or data on demand and that we can organize them in a group and go visiting each of these groups at will; basically this is pagination and we are going to see how we can manually paginate in Laravel without the need to use Eloquent or connections to the database.

In any project in Laravel, we have different mechanisms to do the pagination according to the official documentation, both automatic and to paginate a set of data or lists easily.

Let's start by defining what pagination is.

What is pagination?

Pagination in Laravel is a functionality integrated into this type of framework as Laravel is; using paging, you can split a large data set into chunks; that is, if we have 30 records and we want to paginate by 10, we have 3 groups or pages; to move between pages, an index is used.

Although it seems little, pagination is an ideal method to simplify the records to be displayed; let's suppose that we don't have 30 records, but about 500 or more, and you want to show a paged list of 10 levels again, using pagination we can divide all the records pull and show 10 by 10; to go to another group, we simply click on a link and go to the next group; this avoids having an extremely heavy page, with 500 or more records, avoids generating such a large page and what it entails loading it.

This is accomplished using Eloquent's paginate() or simplePaginate() methods, which take care of cutting the query results into multiple pages based on the number of records we want to display on each one.

Pagination is a number of pages that corresponds to the number of pointed links that generally appears at the bottom of a web page that serves to separate content and separate content into parts.

To use pagination, we use something like:

Post::pagination(10)

To have a 10 level pagination, additionally, you can put more Eloquent methods like where to customize the query.

Another example of pagination in Laravel

Supongamos que tenemos una tabla de "Posts" en nuestra base de datos que queremos mostrar en una vista. Queremos mostrar solo 10 resultados por página.

En el controlador:

public function index()
{
    $posts = DB::table('posts')->paginate(10);
    return view('posts.index', ['posts' => $posts]);
}

In the view:

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

{{ $posts->links() }}

The paginate() method divides the query into pages of 10 elements each, and the links() method generates the links to navigate between the different pages. Thus, in the view, the first 10 results and a set of links to access the following pages will be displayed.

This is just a basic example, Laravel offers us several options to customize the appearance and behavior of the pagination to adapt it to our needs, some of which we will see in the following sections.

Manual pagination in Laravel

For this, we have a couple of very similar classes in Laravel that we can use with a very similar operation, the simplest would be called Paginator and it has the following structure:

__construct($items, $perPage, $currentPage = NULL, array $options = [])

As you can guess, the first two arguments are mandatory, which correspond to the data set and the pagination level (although we are the ones who have to indicate the data to show how we will see a little later).

We are going to work with the following data model:

$elements = range(1, 123);

In which, we simply have an array of 123 elements ranging from 1 to 123.

The simplest pagination we can build looks like this:

$res = new Paginator($currentElements, 10);

In which we only specify the data and how many elements we are going to page; which in this case would be 10; we are going to make a dd of the variable $res:

Paginación simple

Here, as you can see, we have everything we need, the control variables to know data about the pagination and the items.

Paginate with Paginator

If we do a dd of this we saw that we have the previous structure, just with the items to page, now what happens if we want to go to another page; for that we have to indicate a third parameter, if we place the following parameter, we will see that:

$res = new Paginator($currentElements, 10, 3); 
Paginar problema en el currentPage

Basically nothing changes, it continues to give the same result regardless of the pagination level we indicate, which in this case we are indicating that we want to go to page number 3.

If we want to paginate, we have to do it manually; for example:

$currentPage = 3;
$perPage = 10;
 
$currentElements = array_slice($elements, $perPage * ($currentPage - 1), $perPage);
 
$res = new Paginator($currentElements, $perPage, $currentPage);

And if we do a dd:

Paginado correctamente

Now we do have our paginated data.

Dynamic pagination based on passing parameters

As we see a little later, we can pass some parameters to obtain the index of which page our user wants to see, for the moment abstract from this and suppose that we are going to receive this information from our view in a parameter that by default is called page; therefore, since they are data that we are going to receive from our user and that with this we are only going to consult data, therefore, we are going to receive it via get.

So in summary:

$currentPage = $request->page;
$perPage = 10;
 
$currentElements = array_slice($elements, $perPage * ($currentPage - 1), $perPage);
 
$res = new Paginator($currentElements, $perPage, $currentPage);

With this, we can receive which page our user wants to see and build the list to display on demand.

Visualize results in a view

Remember that we also have a function called link that is responsible for painting the pagination links in the view; by default, it uses the Bootstrap layout if you configured your project that way:

@foreach ($res as $r)
    {{ $r }}
@endforeach
 
{{ $res->links() }} 
Enlaces de paginación

Paginate with LengthAwarePaginator

Now, we have another class that we can use which would be the LengthAwarePaginator whose only difference would be that it receives all the elements with which we are going to work:

__construct($items, $total, $perPage, $currentPage = NULL, array $options = [])

So, let's take our example to:

$currentPage = $request->page;
$perPage = 10;
 
$currentElements = array_slice($elements, $perPage * ($currentPage - 1), $perPage);
 
$res = new LengthAwarePaginator($currentElements, count($elements), $perPage, $currentPage);

Otherwise everything is exactly the same; let's make a dd of the above:

Length Paginator

Options for pagination

For both variants, we have one more parameter that would be to do the pagination:

$res = new LengthAwarePaginator($currentElements, count($elements), $perPage, $currentPage, ['path' => url('dashboard/category')]);

Or simply:

$res->setPath('/dashboard/category');

This would be used by the links function in our view to place as part of the path in the navigation links:

Length Paginator Path

Laravel: Redirect back with all previous parameters

One of the aspects that can be a small stone in the shoe is that when we create our fantastic CRUD, with the best design, best validations and the best performance in general, when we decide to edit a record that is NOT from the first page of the list when we finish editing the record, the classic operation is to send it to the listing page... The problem is that we send it to page one of the listing and NOT to the page where we edit the record: I'm going to present you a simple scheme that you can use in any framework, or even without using a framework.

So, if you use Django, CodeIgniter, Flask, or Laravel which is our case study, you can use this solution even if you use Inertia, Livewire or basic Laravel.

Using the a session to save the current page

The trick is to always save somewhere, for example in the session when we are on the listing page; i.e. the complete listing page with parameters and everything: in Laravel it would be something like the following:

//echo request()->fullUrl();
//if(!Str::contains(request()->fullUrl(), ["livewire/message","???"]))
Session::put("url_return",request()->fullUrl());

For example, you can put it in the index (or you should put it...) if you are using basic Laravel with all possible filters and page parameters.

    public function index()
    {
        $posts = Post::orderBy('created_at', 'desc')->paginate(10);
        // select * from posts
        return view('dashboard.post.index', ['posts' => $posts]);
    }

With fullUrl() we obtain the full URL, with EVERYTHING including the parameters, and we save this in the session, a serious extra for livewire or inertia, that when sending requests behind control or internal to this package, you have to filter these occurrences and that's what the conditional you see in the code above is for.

On the edit page

Now once, when we're on the edit page, editing that record from page 2 or higher, we ask if we have anything in the session, from the parameter we set earlier, and if so, we send our user to that page and everything:

if(session("url_return"))
return redirect(session("url_return"));
//Session::put('url_return', null);

You could also clear the session in some key part of your app.

In the case of basic Laravel, it would be the update function for this purpose:

public function update(StorePostPost $request, Post $post)
{
     $post->update($request->validated());
     return back()->with('status', 'Post actualizado con éxito');
}

You could also clear the session in some key part of your app.

In the case of basic Laravel, it would be the update function for this purpose:

I recommend you review how to use many-to-many relationships in Laravel, now that you know how to paginate many records.

I agree to receive announcements of interest about this Blog.

We are going to know how we can establish a manual pagination on a group of elements using Laravel with the Paginator and LengthAwarePaginator classes, also we see how to redirect to the previous page with parameters included.

| 👤 Andrés Cruz

🇪🇸 En español