En el 2015, publicamos un tutorial sobre cómo implementar paginación en PHP con MySQL, que tuvo una gran acogida entre los desarrolladores que buscaban simplificar la navegación en sus aplicaciones. La paginación es una técnica fundamental en el desarrollo web, especialmente cuando se trabaja con grandes volúmenes de datos que no pueden cargarse en una sola página sin afectar el rendimiento del sitio y la experiencia del usuario. Aunque en su momento el tutorial fue útil para muchos, sabemos que el mundo del desarrollo web está en constante evolución.
Por ello, decidimos actualizar este tutorial para adaptarlo a las necesidades actuales, integrando nuevas herramientas y conceptos modernos como Bootstrap 5 para mejorar el diseño y la accesibilidad, y el uso de AJAX para una navegación más fluida sin recargar la página. Además, hemos incorporado filtros dinámicos para que los usuarios puedan buscar y acotar la lista de datos en función de criterios específicos. Este post actualizado te guiará paso a paso en la implementación de una paginación eficaz, optimizando la experiencia de tus usuarios y ayudándote a gestionar los datos de manera más organizada y amigable.
A continuación, te explicaremos los componentes principales del código y cómo funcionan en conjunto. A través de este tutorial, te proporcionaremos el código completo para implementar una paginación interactiva con PHP, MySQL y Bootstrap 5, junto con las funcionalidades de búsqueda y filtros aplicados a una lista de países.
Paso 1: Crear la Base de Datos y la Tabla
Primero, crea la base de datos y la tabla en MySQL:
1 2 3 4 5 6 7 8 9 10 |
CREATE DATABASE test_paginacion_bs5; USE test_paginacion_bs5; CREATE TABLE `paises` ( `codigo` VARCHAR(10) NOT NULL, `nombre` VARCHAR(100) NOT NULL, `moneda` VARCHAR(50) NOT NULL, `capital` VARCHAR(100) NOT NULL, `continente` VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
Llena la tabla paises
con datos para que tengas registros que paginar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
INSERT INTO `paises` (`codigo`, `nombre`, `moneda`, `capital`, `continente`) VALUES ('ARG', 'Argentina', 'Peso argentino', 'Buenos Aires', 'América del Sur'), ('AUS', 'Australia', 'Dólar australiano', 'Canberra', 'Oceanía'), ('BRA', 'Brasil', 'Real', 'Brasilia', 'América del Sur'), ('CAN', 'Canadá', 'Dólar canadiense', 'Ottawa', 'América del Norte'), ('CHL', 'Chile', 'Peso chileno', 'Santiago', 'América del Sur'), ('CHN', 'China', 'Yuan', 'Pekín', 'Asia'), ('COL', 'Colombia', 'Peso colombiano', 'Bogotá', 'América del Sur'), ('CRI', 'Costa Rica', 'Colón costarricense', 'San José', 'América Central'), ('CUB', 'Cuba', 'Peso cubano', 'La Habana', 'América Central'), ('DEU', 'Alemania', 'Euro', 'Berlín', 'Europa'), ('ECU', 'Ecuador', 'Dólar estadounidense', 'Quito', 'América del Sur'), ('EGY', 'Egipto', 'Libra egipcia', 'El Cairo', 'África'), ('ESP', 'España', 'Euro', 'Madrid', 'Europa'), ('FRA', 'Francia', 'Euro', 'París', 'Europa'), ('GTM', 'Guatemala', 'Quetzal', 'Ciudad de Guatemala', 'América Central'), ('HND', 'Honduras', 'Lempira', 'Tegucigalpa', 'América Central'), ('IND', 'India', 'Rupia india', 'Nueva Delhi', 'Asia'), ('ITA', 'Italia', 'Euro', 'Roma', 'Europa'), ('JPN', 'Japón', 'Yen', 'Tokio', 'Asia'), ('MEX', 'México', 'Peso mexicano', 'Ciudad de México', 'América del Norte'), ('NIC', 'Nicaragua', 'Córdoba', 'Managua', 'América Central'), ('PAN', 'Panamá', 'Balboa/Dólar estadounidense', 'Ciudad de Panamá', 'América Central'), ('PER', 'Perú', 'Sol', 'Lima', 'América del Sur'), ('PRY', 'Paraguay', 'Guaraní', 'Asunción', 'América del Sur'), ('RUS', 'Rusia', 'Rublo', 'Moscú', 'Europa'), ('SLV', 'El Salvador', 'Dólar estadounidense', 'San Salvador', 'América Central'), ('URY', 'Uruguay', 'Peso uruguayo', 'Montevideo', 'América del Sur'), ('USA', 'Estados Unidos', 'Dólar estadounidense', 'Washington D.C.', 'América del Norte'), ('VEN', 'Venezuela', 'Bolívar', 'Caracas', 'América del Sur'), ('ZAF', 'Sudáfrica', 'Rand', 'Pretoria', 'África'); |
Paso 2: Configurar la Conexión a la Base de Datos (conexion.php
)
Crea un archivo llamado conexion.php
para manejar la conexión a la base de datos:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php $host = 'localhost'; $dbname = 'test_paginacion_bs5'; $username = 'root'; $password = ''; try { $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { die("Error en la conexión: " . $e->getMessage()); } ?> |
Paso 3: Crear el Archivo Principal index.php
Este archivo incluye el formulario de búsqueda y el área donde se mostrarán los resultados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <title>Paginación en PHP con Filtros y AJAX</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-5"> <h2 class="mb-4">Lista de Países - Paginación con PHP y AJAX</h2> <!-- Formulario de búsqueda --> <div class="row mb-3"> <div class="col-md-5"> <input type="text" id="nombre" class="form-control" placeholder="Filtrar por nombre..." onkeyup="cargarPaises(1)"> </div> <div class="col-md-5"> <input type="text" id="capital" class="form-control" placeholder="Filtrar por capital..." onkeyup="cargarPaises(1)"> </div> <div class="col-md-2"> <button class="btn btn-primary w-100" onclick="cargarPaises(1)">Buscar</button> </div> </div> <!-- Div donde se cargarán los datos --> <div id="cargar_datos"> <!-- Los datos de la tabla y la paginación se cargarán aquí mediante AJAX --> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> function cargarPaises(pagina) { const nombre = $('#nombre').val(); const capital = $('#capital').val(); $.ajax({ url: 'paises_ajax.php', type: 'GET', data: { pagina: pagina, nombre: nombre, capital: capital }, success: function(response) { $('#cargar_datos').html(response); } }); } // Cargar la primera página al cargar la página $(document).ready(function() { cargarPaises(1); }); </script> </body> </html> |
Paso 4: Crear la Lógica de Paginación (paginar_datos.php
)
Este archivo generará los botones de paginación según la cantidad de páginas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<?php function generarPaginacion($pagina, $totalPaginas) { // Asegurarse de que la página actual esté dentro del rango válido if ($pagina < 1) $pagina = 1; if ($pagina > $totalPaginas) $pagina = $totalPaginas; // Generar el HTML para los controles de paginación $html = '<nav aria-label="Page navigation"> <ul class="pagination justify-content-center">'; // Mostrar el botón de "Primera" solo si hay más de 10 páginas y la página actual no es la primera if ($totalPaginas > 10 && $pagina > 1) { $html .= '<li class="page-item"> <button class="page-link" onclick="cargarPaises(1)">Primera</button> </li>'; } // Botón de "Anterior" if ($pagina > 1) { $html .= '<li class="page-item"> <button class="page-link" onclick="cargarPaises(' . ($pagina - 1) . ')">Anterior</button> </li>'; } // Determinar el rango de páginas a mostrar (máximo 10 páginas) $rangoInicio = max(1, $pagina - 5); // Página inicial del rango $rangoFin = min($totalPaginas, $pagina + 4); // Página final del rango // Mostrar las páginas dentro del rango calculado for ($i = $rangoInicio; $i <= $rangoFin; $i++) { $html .= '<li class="page-item ' . ($i == $pagina ? 'active' : '') . '"> <button class="page-link" onclick="cargarPaises(' . $i . ')">' . $i . '</button> </li>'; } // Botón de "Siguiente" if ($pagina < $totalPaginas) { $html .= '<li class="page-item"> <button class="page-link" onclick="cargarPaises(' . ($pagina + 1) . ')">Siguiente</button> </li>'; } // Mostrar el botón de "Última" solo si hay más de 10 páginas y la página actual no es la última if ($totalPaginas > 10 && $pagina < $totalPaginas) { $html .= '<li class="page-item"> <button class="page-link" onclick="cargarPaises(' . $totalPaginas . ')">Última</button> </li>'; } $html .= '</ul></nav>'; return $html; } ?> |
Paso 5: Crear el Archivo que Genera los Datos y Paginación (paises_ajax.php
)
Este archivo recibe los parámetros de paginación y filtros, consulta la base de datos y genera el HTML.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<?php require 'conexion.php'; require 'paginar_datos.php'; // Parámetros de paginación $porPagina = 10; $pagina = isset($_GET['pagina']) ? (int)$_GET['pagina'] : 1; $inicio = ($pagina > 1) ? ($pagina - 1) * $porPagina : 0; // Parámetros de filtro $nombreFiltro = isset($_GET['nombre']) ? '%' . $_GET['nombre'] . '%' : '%'; $capitalFiltro = isset($_GET['capital']) ? '%' . $_GET['capital'] . '%' : '%'; // Contar el total de registros que cumplen con los filtros $sqlCount = "SELECT COUNT(*) AS total FROM paises WHERE nombre LIKE :nombre AND capital LIKE :capital"; $stmtCount = $pdo->prepare($sqlCount); $stmtCount->bindParam(':nombre', $nombreFiltro, PDO::PARAM_STR); $stmtCount->bindParam(':capital', $capitalFiltro, PDO::PARAM_STR); $stmtCount->execute(); $totalRegistros = $stmtCount->fetch(PDO::FETCH_ASSOC)['total']; $totalPaginas = ceil($totalRegistros / $porPagina); // Obtener los registros para la página actual $sql = "SELECT * FROM paises WHERE nombre LIKE :nombre AND capital LIKE :capital LIMIT :inicio, :porPagina"; $stmt = $pdo->prepare($sql); $stmt->bindParam(':nombre', $nombreFiltro, PDO::PARAM_STR); $stmt->bindParam(':capital', $capitalFiltro, PDO::PARAM_STR); $stmt->bindParam(':inicio', $inicio, PDO::PARAM_INT); $stmt->bindParam(':porPagina', $porPagina, PDO::PARAM_INT); $stmt->execute(); $paises = $stmt->fetchAll(PDO::FETCH_ASSOC); ?> <!-- Generar HTML para la tabla --> <div class="table-responsive"> <table class="table table-bordered table-striped"> <thead class="table-dark"> <tr> <th>Código</th> <th>Nombre</th> <th>Moneda</th> <th>Capital</th> <th>Continente</th> </tr> </thead> <tbody> <?php foreach ($paises as $pais): ?> <tr> <td><?php echo htmlspecialchars($pais['codigo']); ?></td> <td><?php echo htmlspecialchars($pais['nombre']); ?></td> <td><?php echo htmlspecialchars($pais['moneda']); ?></td> <td><?php echo htmlspecialchars($pais['capital']); ?></td> <td><?php echo htmlspecialchars($pais['continente']); ?></td> </tr> <?php endforeach; ?> </tbody> </table> </div> <!-- Mostrar el mensaje de registros --> <p class='text-center'> Mostrando registros del <?php echo (($pagina - 1) * $porPagina) + 1; ?> al <?php echo min($pagina * $porPagina, $totalRegistros); ?> de un total de <?php echo $totalRegistros; ?> registros. </p> <!-- Generar los controles de paginación --> <?php echo generarPaginacion($pagina, $totalPaginas); ?> |
Esperamos que esta versión actualizada sea tan útil como la original y que puedas aplicarla en tus proyectos para brindar una experiencia de usuario optimizada.