Paginación manual y personalizada en Laravel (sin Eloquent)

- 👤 Andrés Cruz

🇺🇸 In english

Paginación manual y personalizada en Laravel (sin Eloquent)

Muchas veces necesitamos tener algo de organización o agrupación sobre algún conjunto de elementos, ya sea que venga de la base de datos o por ejemplo, datos que vienen de una API empleando el Cliente HTTP de Laravel, y con organización me refiero a poder mostrar un conjunto de estos elementos o datos bajo demanda y que podamos organizarlos en grupo e ir visitando cada uno de estos grupos a placer; básicamente esto es la paginación y vamos a ver cómo podemos paginar de manera manual en Laravel sin necesidad de emplear Eloquent o conexiones a la base de datos.

En cualquier proyecto en Laravel, tenemos distintos mecanismos para hacer la paginación acorde a la documentación oficial, tanto automática como paginar un conjunto de datos o listados fácilmente.

Comencemos definiendo que es la paginación

¿Qué es la paginación?

La paginación en Laravel es una funcionalidad integrada en este tipo de frameworks como lo es Laravel; empleando la paginación, se puede dividir un conjunto grande de datos en trozos; es decir, que si tenemos 30 registros y queremos paginar de 10 en 10, tendríamos 3 grupos o páginas; para moverse entre las páginas, se usa un indice.

Aunque parece poco, la paginación es un método ideal para simplificar los registros a mostrar; supongamos que no tenemos 30 registros, si no unos 500 o más, y quieres mostrar un listado pagina do nuevamente de 10 niveles, mediante la paginación podemos dividir todo el pull de registros e ir mostrando de 10 en 10; para pasar a otro grupo, simplemente precionamos sobre un enlace y vamos al siguiente grupo; esto evita tener una página extremadamente pesada, con 500 o más registros, evita generar un página tan grande y lo que conyeva cargar la misma.

Esto se logra utilizando los métodos paginate() o simplePaginate() de Eloquent, que se encargan de cortar los resultados de la consulta en varias páginas según el número de registros que queremos mostrar en cada una.

La paginación es un número de páginas que corresponde con la cantidad de enlaces señalados que generalmente aparece en la parte de abajo de una página web que sirve para separar el contenido y separar el contenido en partes.

Para usar la paginación, usamos algo como:

Post::pagination(10)

Para tener una paginación de 10 niveles, adicionalmente, puedes colocar más métodos de Eloquent como where para personalizar la consulta.

Otro ejemplo de paginación en 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]);
}

En la vista:

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

{{ $posts->links() }}

El método paginate() divide la consulta en páginas de 10 elementos cada una, y el método links() genera los enlaces para navegar entre las distintas páginas. Así, en la vista, se mostrarán los primeros 10 resultados y un conjunto de enlaces para acceder a las páginas siguientes.

Este es solo un ejemplo básico, Laravel nos ofrece varias opciones para personalizar la apariencia y comportamiento de la paginación para adaptarla a nuestras necesidades que veremos algunas en los siguientes apartados.

Paginación manual en Laravel

Para esto, tenemos un par de clases muy parecidas en Laravel que podemos emplear con un funcionamiento muy parecido, la más sencilla, sería la llamada Paginator y tiene la siguiente estructura:

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

Como puedes suponer, los primeros dos argumentos son obligatorios, que corresponden al conjunto de datos y el nivel de paginación (aunque somos nosotros los que tenemos que indicar los datos a mostrar cómo vamos a ver un poco más adelante).

Vamos a trabajar con el siguiente modelo de datos:

$elements = range(1, 123);

En el cual, simplemente tenemos un array de 123 elementos que van desde 1 a 123.

La paginación más sencilla que podemos construir luce de la siguiente manera:

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

En la cual solamente especificamos los datos y cuantos elementos vamos a paginar; que en esta caso serían 10; vamos ha hacer un dd de la variable $res:

Paginación simple

Aquí como puedes ver, tenemos todo lo que necesitamos, las variables de control para saber datos sobre la paginación y los items.

Paginar con Paginate

Si hacemos un dd de esto vimos que tenemos la estructura anterior, justo con los items a paginar, ahora qué pasa si queremos ir a otra página; para eso tenemos que indicar un tercer parámetro, si colocamos el siguiente parámetro, veremos que:

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

Básicamente nada cambia, sigue dando el mismo resultado sin importar que nivel de paginación indiquemos, que en este caso estamos indicando que queremos ir a la página número 3.

Si queremos paginar, tenemos que hacerlo de manera manual; por ejemplo:

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

Y si hacemos un dd:

Paginado correctamente

Ahora sí tenemos nuestros datos paginados.

Paginación dinámica en base a pase de parámetros

Como vemos un poco más adelante, podemos pasar algunos parámetros para obtener el índice de que pagina quiere ver nuestro usuario, de momento abstraerse  de esto y suponte que vamos a recibir esta información desde nuestra vista en un parámetro que por defecto se llama page; por lo tanto, como son datos que vamos a recibir de nuestro usuario y que con esto solamente vamos a consultar datos, por lo tanto, vamos a recibirlo vía get.

Así que en resumen:

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

Con esto, podemos recibir cual página quiere ver nuestro usuario y construir el listado a mostrar bajo demanda.

Visualizar resultados en una vista

Recuerda que nosotros también tenemos una función llamada link que se encarga de pintar los enlaces de paginación en la vista; por defecto, emplea el diseño de Bootstrap si así configurastes tu proyecto:

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

Paginar con LengthAwarePaginator

Ahora, tenemos otra clase que podemos emplear que sería la de LengthAwarePaginator cuya única diferencia sería que la misma recibe el total de los elementos con los cuales vamos a trabajar:

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

Por lo tanto, vamos a llevar nuestro ejemplo a:

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

Por lo demás todo es exactamente igual; vamos a hacer un dd de lo anterior:

Length Paginator

Opciones para la paginación

Para ambas variantes, tenemos una parámetro más que sería para hacer la paginación:

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

O simplemente:

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

Esto se emplearía por la función de links en nuestra vista para colocar como parte del path en los enlaces de navegación:

Length Paginator Path

Laravel: Redirigir hacia atrás con todos los parámetros anteriores

Laravel: Redirigir hacia atrás con todos los parámetros anteriores

Uno de los aspectos que puede ser una pequeña piedra en el zapato es que cuando creamos nuestro fantástico CRUD, con el mejor diseño, mejores validaciones y el mejor funcionamiento en general, cuando decidimos editar un registro que NO sea de la primera página del listado cuando terminamos de editar el registro, el funcionamiento clásico es enviarlo a la página de listado... El problema es que lo enviamos a la página uno del listado y NO a la página en la cual editamos el registro: te voy a presentar un sencillo esquema que puedes emplear en cualquier framework, o inclusive sin necesidad de emplear un framework.

Así que, si empleas Django, CodeIgniter, Flask, o Laravel que es nuestro caso de estudio, puedes emplear esta solución inclusive si empleas Inertia, Livewire o Laravel básico.

El uso del a sesión para guardar la página actual

El truco es siempre guardar en alguna parte, por ejemplo en la sesión cuando estamos en la página de listado; es decir, la página de listado completa con parámetros y todo: en Laravel sería algo como lo siguiente:

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

Por ejemplo, lo puede colocar en el index (o lo debes colocar...) si estás empleando laravel básico con todos los posibles filtros y parámetros de la página.

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

Con fullUrl() obtenemos la URL full, con TODO incluido los parámetros, y esto lo guardamos en la sesión, un extra seria para livewire o inertia, que al enviar peticiones por detrás de control o internas a este paquete, tienes que filtrar estas ocurrencias y para eso es el condicional que ves en el código anterior.

En la página de edición

Ahora una vez, cuando estemos en la página de edición, editando ese registro de la página 2 o superior, preguntamos si tenemos algo en la sesión, del parámetro que configuramos anteriormente, y si es así, mandamos a nuestro usuario para esa pagina y todo el mundo feliz:

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

También pudieras limpiar la sesión en alguna parte clave de tu app.

En el caso de Laravel básico, sería la función de update para tal fin:

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

Por lo demás, con este sencillo esquema puedes realizar lo comentado inicialmente: si lo quieres ver paso a paso, puedes ver el video al inicio de esta publicación.

Te recomiendo que revisas como emplear las relaciones muchos a muchos en Laravel, ahora que ya sabes como paginar muchos registros.

Acepto recibir anuncios de interes sobre este Blog.

Vamos a conocer como podemos establecer una paginación manual sobre un grupo de elementos empleando Laravel con las clases Paginator y LengthAwarePaginator y también como redireccionar a la página anterior con parámetros incluidos.

| 👤 Andrés Cruz

🇺🇸 In english