Paginación con PHP y MySQL

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.

Algoritmo para paginar resultados

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.

La base de datos

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.

Conexión PHP a base de datos

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.

Paginación con PHP y MySQL

Paginación con PHP y MySQL

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">&laquo;</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">&raquo;</span>
                </a>
            </li>
        <?php } ?>
    </ul>
</nav>

Poniendo todo junto

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">&laquo;</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">&raquo;</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.

Conclusión

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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

3 comentarios en “Paginación con PHP y MySQL”

  1. 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;
    }

  2. 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.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *