En este post te mostraré de manera muy simple y sencilla cómo hacer una paginación de datos de MySQL usando PHP. Esto quiere decir que muestras los datos de la tabla por página, mostrando ciertos elementos por página.
Además de paginar los datos por tabla, también te enseñaré cómo crear enlaces que lleven a determinado número de página, cómo saber cuántas páginas puedes tener, etcétera.
Verás que la explicación es muy sencilla y después de esto podrás adaptar el código a tu software o tomar el ejemplo como está.
Nota: para este tutorial asumo que ya tienes las nociones básicas de PHP con MySQL, pero si no, no te preocupes, te invito a ver cómo conectar PHP con MySQL usando PDO y también te invito a ver un sistema de ventas en PHP para reafirmar tus conocimientos.
Primero vamos a analizar cómo es que funciona todo esto. Podemos tener una simple fórmula que nos permitirá sacar el número de páginas disponibles:
Páginas disponibles = RedondearHaciaArriba(Cantidad de datos / Datos a mostrar por página)
Estamos redondeando hacia arriba porque en ocasiones el resultado será un valor decimal, y si mostramos el resultado como tal, vamos a omitir algunos resultados porque las páginas no serán suficientes.
Ahora veamos cómo obtener esos datos. La cantidad de datos se puede obtener con un COUNT
de SQL, y los datos a mostrar por página los definimos nosotros.
Para redondear hacia arriba usamos la función ceil
que PHP ya ofrece. Y finalmente para obtener solo algunos valores usamos LIMIT
y OFFSET
de SQL igualmente.
LIMIT nos permite limitar los resultados, y OFFSET nos permite saltar determinados resultados. Por lo tanto LIMIT siempre será igual a la cantidad de datos a mostrar por página.
Por otro lado, el OFFSET se puede calcular con:
Offset = (Página actual - 1) * Datos por página;
Necesitamos restarle 1 porque en el caso de que la página sea 0, necesitamos que el OFFSET sea 0, pero para todo lo demás, necesitamos que sea la página – 1 multiplicada por los datos.
Con este enfoque, evitamos cortar arreglos y ahorramos memoria, aunque hacemos dos consultas a la base de datos: una para obtener el conteo, y otra para obtener los datos reales.
Dicho esto, veamos el código.
Para ejemplificar esto, tengo una simple base de datos que a su vez tiene una tabla de productos. La estructura, creación de base de datos y algunos datos de prueba se ven así:
DROP DATABASE IF EXISTS ventas;
CREATE DATABASE IF NOT EXISTS ventas;
USE ventas;
CREATE TABLE productos(
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
codigo VARCHAR(255) NOT NULL,
descripcion VARCHAR(255) NOT NULL,
precioVenta DECIMAL(5, 2) NOT NULL,
precioCompra DECIMAL(5, 2) NOT NULL,
existencia DECIMAL(5, 2) NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;
INSERT INTO productos(id, codigo, descripcion, precioVenta, precioCompra, existencia)
VALUES
(1, '1', 'Galletas chokis', 15, 10, 100),
(2, '2', 'Mermelada de fresa', 80, 65, 100),
(3, '3', 'Aceite', 20, 18, 100),
(4, '4', 'Palomitas de maíz', 15, 12, 100),
(5, '5', 'Doritos', 8, 5, 100),
(6, '6', 'Electrolit suero rehidratante', 50, 60, 100),
(7, '7', 'Teclado HyperX', 2000, 1800, 100),
(8, '8', 'Mouse Logitech', 700, 500, 100),
(9, '9', 'Audífonos Xiaomi', 400, 300, 100),
(10, '10', 'Monitor Samsung', 2300, 2000, 100);
# Correcto
Por el momento no te preocupes por la estructura de la tabla, al final de todo lo que nos interesa es hacer una paginación con PHP y MySQL.
El archivo de conexión es el siguiente:
<?php
$contraseña = "";
$usuario = "root";
$nombre_base_de_datos = "ventas";
try {
$base_de_datos = new PDO('mysql:host=localhost;dbname=' . $nombre_base_de_datos, $usuario, $contraseña);
$base_de_datos->query("set names utf8;");
$base_de_datos->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$base_de_datos->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$base_de_datos->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
} catch (Exception $e) {
echo "Ocurrió algo con la base de datos: " . $e->getMessage();
}
?>
Este archivo lo vamos a usar para traer los valores. Recuerda cambiar las credenciales de acuerdo a tu entorno.
Ahora sí veamos el código. Definimos cuántos elementos vamos a mostrar por página:
<?php
# Cuántos productos mostrar por página
$productosPorPagina = 3;
El número de página se obtendrá de la URL pero si no se proporciona se asume que es la página 1:
<?php
// Por defecto es la página 1; pero si está presente en la URL, tomamos esa
$pagina = 1;
if (isset($_GET["pagina"])) {
$pagina = $_GET["pagina"];
}
Ahora calculamos el limit y offset:
<?php
# El límite es el número de productos por página
$limit = $productosPorPagina;
# El offset es saltar X productos que viene dado por multiplicar la página - 1 * los productos por página
$offset = ($pagina - 1) * $productosPorPagina;
Obtenemos el conteo de los productos, para ello hacemos un count. Usamos as para renombrar la columna.
<?php
# Necesitamos el conteo para saber cuántas páginas vamos a mostrar
$sentencia = $base_de_datos->query("SELECT count(*) AS conteo FROM productos");
$conteo = $sentencia->fetchObject()->conteo;
Calculamos cuántas páginas tenemos:
<?php
# Para obtener las páginas dividimos el conteo entre los productos por página, y redondeamos hacia arriba
$paginas = ceil($conteo / $productosPorPagina);
Finalmente obtenemos los productos:
<?php
# Ahora obtenemos los productos usando ya el OFFSET y el LIMIT
$sentencia = $base_de_datos->prepare("SELECT * FROM productos LIMIT ? OFFSET ?");
$sentencia->execute([$limit, $offset]);
$productos = $sentencia->fetchAll(PDO::FETCH_OBJ);
# Y más abajo los dibujamos...
Luego los mostramos en una tabla:
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Código</th>
<th>Descripción</th>
<th>Precio de compra</th>
<th>Precio de venta</th>
<th>Existencia</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $producto) { ?>
<tr>
<td><?php echo $producto->id ?></td>
<td><?php echo $producto->codigo ?></td>
<td><?php echo $producto->descripcion ?></td>
<td><?php echo $producto->precioCompra ?></td>
<td><?php echo $producto->precioVenta ?></td>
<td><?php echo $producto->existencia ?></td>
</tr>
<?php } ?>
</tbody>
</table>
Finalmente creamos los enlaces para navegar entre páginas. Además de los enlaces que llevan directamente a una página, mostramos el de página siguiente y página anterior. También mostramos la página actual y cuántas páginas existen.
<nav>
<div class="row">
<div class="col-xs-12 col-sm-6">
<p>Mostrando <?php echo $productosPorPagina ?> de <?php echo $conteo ?> productos disponibles</p>
</div>
<div class="col-xs-12 col-sm-6">
<p>Página <?php echo $pagina ?> de <?php echo $paginas ?> </p>
</div>
</div>
<ul class="pagination">
<!-- Si la página actual es mayor a uno, mostramos el botón para ir una página atrás -->
<?php if ($pagina > 1) { ?>
<li>
<a href="./listar.php?pagina=<?php echo $pagina - 1 ?>">
<span aria-hidden="true">«</span>
</a>
</li>
<?php } ?>
<!-- Mostramos enlaces para ir a todas las páginas. Es un simple ciclo for-->
<?php for ($x = 1; $x <= $paginas; $x++) { ?>
<li class="<?php if ($x == $pagina) echo "active" ?>">
<a href="./listar.php?pagina=<?php echo $x ?>">
<?php echo $x ?></a>
</li>
<?php } ?>
<!-- Si la página actual es menor al total de páginas, mostramos un botón para ir una página adelante -->
<?php if ($pagina < $paginas) { ?>
<li>
<a href="./listar.php?pagina=<?php echo $pagina + 1 ?>">
<span aria-hidden="true">»</span>
</a>
</li>
<?php } ?>
</ul>
</nav>
Al final el código queda así:
<?php
# Este es el simple encabezado HTML
include_once "encabezado.php";
# Incluimos la conexión
include_once "base_de_datos.php";
# Cuántos productos mostrar por página
$productosPorPagina = 3;
// Por defecto es la página 1; pero si está presente en la URL, tomamos esa
$pagina = 1;
if (isset($_GET["pagina"])) {
$pagina = $_GET["pagina"];
}
# El límite es el número de productos por página
$limit = $productosPorPagina;
# El offset es saltar X productos que viene dado por multiplicar la página - 1 * los productos por página
$offset = ($pagina - 1) * $productosPorPagina;
# Necesitamos el conteo para saber cuántas páginas vamos a mostrar
$sentencia = $base_de_datos->query("SELECT count(*) AS conteo FROM productos");
$conteo = $sentencia->fetchObject()->conteo;
# Para obtener las páginas dividimos el conteo entre los productos por página, y redondeamos hacia arriba
$paginas = ceil($conteo / $productosPorPagina);
# Ahora obtenemos los productos usando ya el OFFSET y el LIMIT
$sentencia = $base_de_datos->prepare("SELECT * FROM productos LIMIT ? OFFSET ?");
$sentencia->execute([$limit, $offset]);
$productos = $sentencia->fetchAll(PDO::FETCH_OBJ);
# Y más abajo los dibujamos...
?>
<div class="col-xs-12">
<h1>Productos</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Código</th>
<th>Descripción</th>
<th>Precio de compra</th>
<th>Precio de venta</th>
<th>Existencia</th>
</tr>
</thead>
<tbody>
<?php foreach ($productos as $producto) { ?>
<tr>
<td><?php echo $producto->id ?></td>
<td><?php echo $producto->codigo ?></td>
<td><?php echo $producto->descripcion ?></td>
<td><?php echo $producto->precioCompra ?></td>
<td><?php echo $producto->precioVenta ?></td>
<td><?php echo $producto->existencia ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<nav>
<div class="row">
<div class="col-xs-12 col-sm-6">
<p>Mostrando <?php echo $productosPorPagina ?> de <?php echo $conteo ?> productos disponibles</p>
</div>
<div class="col-xs-12 col-sm-6">
<p>Página <?php echo $pagina ?> de <?php echo $paginas ?> </p>
</div>
</div>
<ul class="pagination">
<!-- Si la página actual es mayor a uno, mostramos el botón para ir una página atrás -->
<?php if ($pagina > 1) { ?>
<li>
<a href="./listar.php?pagina=<?php echo $pagina - 1 ?>">
<span aria-hidden="true">«</span>
</a>
</li>
<?php } ?>
<!-- Mostramos enlaces para ir a todas las páginas. Es un simple ciclo for-->
<?php for ($x = 1; $x <= $paginas; $x++) { ?>
<li class="<?php if ($x == $pagina) echo "active" ?>">
<a href="./listar.php?pagina=<?php echo $x ?>">
<?php echo $x ?></a>
</li>
<?php } ?>
<!-- Si la página actual es menor al total de páginas, mostramos un botón para ir una página adelante -->
<?php if ($pagina < $paginas) { ?>
<li>
<a href="./listar.php?pagina=<?php echo $pagina + 1 ?>">
<span aria-hidden="true">»</span>
</a>
</li>
<?php } ?>
</ul>
</nav>
</div>
<?php include_once "pie.php" ?>
Si te fijas, además del código he incluido el encabezado y pie que no hacen otra cosa más que agregar el menú de navegación, cerrar etiquetas, etcétera. Esto es porque he tomado como base el proyecto de punto de venta con PHP.
Para terminar te dejo el código completo en GitHub. Espero que este ejemplo te haya servido para que puedas implementar tu propia paginación, o que te haya servido como base.
Si te gusta el lenguaje, te invito a ver más sobre PHP en mi blog.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.
Ver comentarios
Interesante, No uso php puro años pero lo entiendo. Estoy usando Codeigniter 3 un MVC pero para su paginacion se hace un mundo llamando a librerias internas etc. Lo bueno que puedes implementar codigo puro php. Me sirvio tu codigo solo tuve que cambiarle ciertas cosas como ejemplo el codigo php para las consultas sql ya que no lo usamos asi. pero todo lo demas normal y me funciono! gracias
Como lo termine asi:
# Cuántos productos mostrar por página
$productosPorPagina = 24;
// Por defecto es la página 1; pero si está presente en la URL, tomamos esa
$pagina = $this->uri->segment(3,0);
if($pagina==0 or $pagina=='' or $pagina==null){
redirect(base_url().'inicio/main/1');
}
# El límite es el número de productos por página
$limit = $productosPorPagina;
# El offset es saltar X productos que viene dado por multiplicar la página - 1 * los productos por página
$offset = ($pagina - 1) * $productosPorPagina;
$sql = $this->db->query('select count(*) as conteo
from tab_producto p where Estado=0 and Catalogo=0');
$data_1 = $sql->row_array();
$conteo = $data_1['conteo'];
# Para obtener las páginas dividimos el conteo entre los productos por página, y redondeamos hacia arriba
$paginas = ceil($conteo / $productosPorPagina);
$query = $this->db->query("select *,
(select Url from tab_imagenes where Posicion=1 and Id_producto=p.Id) as imagen_1,
(select Url from tab_imagenes where Posicion=2 and Id_producto=p.Id) as imagen_2
from tab_producto p where Estado=0 and Catalogo=0 limit ".$offset.",".$limit);
foreach ($query->result() as $x)
{
echo $x->Id;
}
Buen día, me gustaría aprender bien este lenguaje ,ya tengo conocimientos básicos.
Pero me interesa mucho ponerlos en practica y aprender mas.
Hola. En mi blog puede encontrar bastante contenido sobre MySQL y PHP
Saludos :)