Tienda de productos - Mostrar botón para agregar o quitar del carrito de compras

Carrito de compras con PHP y MySQL

Hoy vamos a ver cómo implementar un carrito de compras con PHP y MySQL. Vamos a poner una especie de tienda o e-commerce con PHP en donde el usuario puede agregar productos al carrito, ver su carrito de compras con el total, y quitar productos del mismo.

Para ello vamos a usar la sesión y MySQL. Con la sesión vamos a identificar al usuario y la gestión del carrito se hará a través de esta base de datos. Ten en cuenta que aquí el punto es el carrito de compras, no una tienda online.

La única desventaja que tendremos es que si el usuario abandona el carrito de compras los datos no serán eliminados, pero esto se puede arreglar implementando todo el carrito como un array en la sesión tal como lo hice en mi sistema de ventas con PHP.

Te dejaré el código completo al final del post, como siempre, gratuito y open source.

Módulos del sistema

Como lo dije, esto será más que nada un ejemplo. Tendremos el módulo de gestión de productos para agregar y eliminar productos como administradores de la tienda.

Por otro lado existirá el módulo de tienda en donde el usuario podrá agregar productos al carro y más tarde ver los productos que tiene en su cesta así como el total para terminar su compra.

Una vez que el usuario termine su compra, el programador es libre de hacer lo que guste con los productos. Es decir, puede registrar la venta, restar existencia, etcétera.

Base de datos

Antes de comenzar, te mostraré cómo configurar la base de datos. Las tablas son las siguientes:

CREATE TABLE IF NOT EXISTS productos(
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(255) NOT NULL,
    descripcion VARCHAR(1024) NOT NULL,
    precio DECIMAL(9,2)
);
CREATE TABLE IF NOT EXISTS carrito_usuarios(
    id_sesion VARCHAR(255) NOT NULL,
    id_producto BIGINT UNSIGNED NOT NULL,
    FOREIGN KEY (id_producto) REFERENCES productos(id)
    ON UPDATE CASCADE ON DELETE CASCADE
);

Tenemos la tabla de los productos de la tienda y la tabla para la relación entre los productos que el usuario agrega a su cesta. Para identificar al usuario usamos su id de sesión.

Recuerda que debes crear el archivo env.php basándote en el archivo env.ejemplo.php para configurar las credenciales de la base de datos. El mío se ve así:

; <?php exit; ?>
MYSQL_DATABASE_NAME = "tienda"
MYSQL_USER = "root"
MYSQL_PASSWORD = ""

Para obtener la conexión tenemos la siguiente función:

<?php
function obtenerVariableDelEntorno($key)
{
    if (defined("_ENV_CACHE")) {
        $vars = _ENV_CACHE;
    } else {
        $file = "env.php";
        if (!file_exists($file)) {
            throw new Exception("El archivo de las variables de entorno ($file) no existe. Favor de crearlo");
        }
        $vars = parse_ini_file($file);
        define("_ENV_CACHE", $vars);
    }
    if (isset($vars[$key])) {
        return $vars[$key];
    } else {
        throw new Exception("La clave especificada (" . $key . ") no existe en el archivo de las variables de entorno");
    }
}
function obtenerConexion()
{
    $password = obtenerVariableDelEntorno("MYSQL_PASSWORD");
    $user = obtenerVariableDelEntorno("MYSQL_USER");
    $dbName = obtenerVariableDelEntorno("MYSQL_DATABASE_NAME");
    $database = new PDO('mysql:host=localhost;dbname=' . $dbName, $user, $password);
    $database->query("set names utf8;");
    $database->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
    $database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $database->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
    return $database;
}

Gestión de productos

Gestión de productos en PHP - Carrito de compras
Gestión de productos en PHP – Carrito de compras

Comencemos viendo la administración de productos. Las funciones que se encargan de todo ello son las siguientes:

<?php
function eliminarProducto($id)
{
    $bd = obtenerConexion();
    $sentencia = $bd->prepare("DELETE FROM productos WHERE id = ?");
    return $sentencia->execute([$id]);
}

function guardarProducto($nombre, $precio, $descripcion)
{
    $bd = obtenerConexion();
    $sentencia = $bd->prepare("INSERT INTO productos(nombre, precio, descripcion) VALUES(?, ?, ?)");
    return $sentencia->execute([$nombre, $precio, $descripcion]);
}

Todo esto está dentro del archivo de funciones globales. Más tarde vamos a incluir este archivo e invocarlas. Por ejemplo, tenemos el formulario:

<?php include_once "encabezado.php" ?>
<div class="columns">
    <div class="column is-one-third">
        <h2 class="is-size-2">Nuevo producto</h2>
        <form action="guardar_producto.php" method="post">
            <div class="field">
                <label for="nombre">Nombre</label>
                <div class="control">
                    <input required id="nombre" class="input" type="text" placeholder="Nombre" name="nombre">
                </div>
            </div>
            <div class="field">
                <label for="descripcion">Descripción</label>
                <div class="control">
                    <textarea name="descripcion" class="textarea" id="descripcion" cols="30" rows="5" placeholder="Descripción" required></textarea>
                </div>
            </div>
            <div class="field">
                <label for="precio">Precio</label>
                <div class="control">
                    <input required id="precio" name="precio" class="input" type="number" placeholder="Precio">
                </div>
            </div>
            <div class="field">
                <div class="control">
                    <button class="button is-success">Guardar</button>
                    <a href="productos.php" class="button is-warning">Volver</a>
                </div>
            </div>
        </form>
    </div>
</div>
<?php include_once "pie.php" ?>

El mismo será enviado a guardar_producto.php en donde recibimos los datos, incluimos las funciones, invocamos al método necesario y redireccionamos. Así de simple. Este patrón se sigue en los demás archivos.

<?php
if (!isset($_POST["nombre"]) || !isset($_POST["precio"]) || !isset($_POST["descripcion"])) {
    exit("Faltan datos");
}
include_once "funciones.php";
guardarProducto($_POST["nombre"], $_POST["precio"], $_POST["descripcion"]);
header("Location: productos.php");

Tienda

Tienda de productos - Mostrar botón para agregar o quitar del carrito de compras
Tienda de productos – Mostrar botón para agregar o quitar del carrito de compras

Como lo dije, no es una tienda completa. Es más bien un listado de todos los productos, destinada más al usuario que agregará los productos a su carro. En este caso cada producto tiene un botón para agregar al carrito.

También se está verificando si el producto ya está en el carrito de compras, en ese caso se muestra un botón para quitarlo.

El código para mostrar esto es el siguiente:

<?php include_once "encabezado.php" ?>
<?php
include_once "funciones.php";
$productos = obtenerProductos();
?>
<div class="columns">
    <div class="column">
        <h2 class="is-size-2">Tienda</h2>
    </div>
</div>
<?php foreach ($productos as $producto) { ?>

    <div class="columns">
        <div class="column is-full">
            <div class="card">
                <header class="card-header">
                    <p class="card-header-title is-size-4">
                        <?php echo $producto->nombre ?>
                    </p>
                </header>
                <div class="card-content">
                    <div class="content">
                        <?php echo $producto->descripcion ?>
                    </div>
                    <?php if (productoYaEstaEnCarrito($producto->id)) { ?>
                        <h1 class="is-size-3">$<?php echo number_format($producto->precio, 2) ?></h1>
                        <form action="eliminar_del_carrito.php" method="post">
                            <input type="hidden" name="id_producto" value="<?php echo $producto->id ?>">
                            <span class="button is-success">
                                <i class="fa fa-check"></i>&nbsp;En el carrito
                            </span>
                            <button class="button is-danger">
                                <i class="fa fa-trash-o"></i>&nbsp;Quitar
                            </button>
                        </form>
                    <?php } else { ?>
                        <form action="agregar_al_carrito.php" method="post">
                            <input type="hidden" name="id_producto" value="<?php echo $producto->id ?>">
                            <button class="button is-primary">
                                <i class="fa fa-cart-plus"></i>&nbsp;Agregar al carrito
                            </button>
                        </form>
                    <?php } ?>
                </div>
            </div>
        </div>
    </div>
<?php } ?>
<?php include_once "pie.php" ?>

Estamos combinando código de PHP con HTML para mostrar el valor. En este caso usamos las funciones del archivo que mencioné anteriormente y que te dejaré al final del post dentro del código completo.

Estas funciones de las que hablo nos permiten saber si el producto ya está en el carrito, obtener todos los productos, etcétera.

Agregar producto al carrito

Para agregar un producto al carrito en este ejemplo con PHP se muestra un botón dentro de un formulario que lleva el id del producto:

<form action="agregar_al_carrito.php" method="post">
    <input type="hidden" name="id_producto" value="<?php echo $producto->id ?>">
    <button class="button is-primary">
        <i class="fa fa-cart-plus"></i>&nbsp;Agregar al carrito
    </button>
</form>

El action del formulario es agregar_al_carrito.php, ahí recibimos el ID e invocamos a la función pertinente:

<?php
include_once "funciones.php";
if (!isset($_POST["id_producto"])) {
    exit("No hay id_producto");
}
agregarProductoAlCarrito($_POST["id_producto"]);
header("Location: tienda.php");

La verdadera magia está ocurriendo en las funciones:

<?php
function agregarProductoAlCarrito($idProducto)
{
    // Ligar el id del producto con el usuario a través de la sesión
    $bd = obtenerConexion();
    iniciarSesionSiNoEstaIniciada();
    $idSesion = session_id();
    $sentencia = $bd->prepare("INSERT INTO carrito_usuarios(id_sesion, id_producto) VALUES (?, ?)");
    return $sentencia->execute([$idSesion, $idProducto]);
}

Lo que hacemos es obtener la conexión, iniciar la sesión si no está iniciada, obtener el id de sesión de ese usuario e insertar un registro que solo llevará el id de la sesión y el id del producto. Todo eso con sentencias preparadas para evitar inyecciones SQL.

Ya después podemos obtener los productos igualmente a través del id de sesión:

<?php
function obtenerIdsDeProductosEnCarrito()
{
    $bd = obtenerConexion();
    iniciarSesionSiNoEstaIniciada();
    $sentencia = $bd->prepare("SELECT id_producto FROM carrito_usuarios WHERE id_sesion = ?");
    $idSesion = session_id();
    $sentencia->execute([$idSesion]);
    return $sentencia->fetchAll(PDO::FETCH_COLUMN);
}

O también obtener, a través de un INNER JOIN, los detalles de los productos del carrito de compras:

<?php
function obtenerProductosEnCarrito()
{
    $bd = obtenerConexion();
    iniciarSesionSiNoEstaIniciada();
    $sentencia = $bd->prepare("SELECT productos.id, productos.nombre, productos.descripcion, productos.precio
     FROM productos
     INNER JOIN carrito_usuarios
     ON productos.id = carrito_usuarios.id_producto
     WHERE carrito_usuarios.id_sesion = ?");
    $idSesion = session_id();
    $sentencia->execute([$idSesion]);
    return $sentencia->fetchAll();
}

Y para quitar un producto del carrito, eliminamos usando el id del producto y el de sesión:

<?php
function quitarProductoDelCarrito($idProducto)
{
    $bd = obtenerConexion();
    iniciarSesionSiNoEstaIniciada();
    $idSesion = session_id();
    $sentencia = $bd->prepare("DELETE FROM carrito_usuarios WHERE id_sesion = ? AND id_producto = ?");
    return $sentencia->execute([$idSesion, $idProducto]);
}

Te repito, en este caso si el usuario abandona el carrito de compras (es decir, cierra la página y luego la sesión cuenta con otro id) los datos se quedarán ahí por siempre.

Una solución podría ser guardar los productos en la sesión, o guardar la fecha del registro de la última actividad del usuario para eliminar los registros de la base de datos periódicamente.

Carrito de compras

Ver carrito de compras con PHP - Mostrar total y botón para terminar compra
Ver carrito de compras con PHP – Mostrar total y botón para terminar compra

Ya para terminar veamos qué pasa cuando el usuario ve su cesta de compras. Simplemente obtenemos todos los productos que ha agregado y los mostramos en una tabla con un botón para quitarlos.

También mostramos el total a pagar y un botón para terminar la compra. El código es:

<?php include_once "encabezado.php" ?>
<?php
include_once "funciones.php";
$productos = obtenerProductosEnCarrito();
if (count($productos) <= 0) {
?>
    <section class="hero is-info">
        <div class="hero-body">
            <div class="container">
                <h1 class="title">
                    Todavía no hay productos
                </h1>
                <h2 class="subtitle">
                    Visita la tienda para agregar productos a tu carrito
                </h2>
                <a href="tienda.php" class="button is-warning">Ver tienda</a>
            </div>
        </div>
    </section>
<?php } else { ?>
    <div class="columns">
        <div class="column">
            <h2 class="is-size-2">Mi carrito de compras</h2>
            <table class="table">
                <thead>
                    <tr>
                        <th>Nombre</th>
                        <th>Descripción</th>
                        <th>Precio</th>
                        <th>Quitar</th>
                    </tr>
                </thead>
                <tbody>
                    <?php
                    $total = 0;
                    foreach ($productos as $producto) {
                        $total += $producto->precio;
                    ?>
                        <tr>
                            <td><?php echo $producto->nombre ?></td>
                            <td><?php echo $producto->descripcion ?></td>
                            <td>$<?php echo number_format($producto->precio, 2) ?></td>
                            <td>
                                <form action="eliminar_del_carrito.php" method="post">
                                    <input type="hidden" name="id_producto" value="<?php echo $producto->id ?>">
                                    <input type="hidden" name="redireccionar_carrito">
                                    <button class="button is-danger">
                                        <i class="fa fa-trash-o"></i>
                                    </button>
                                </form>
                            </td>
                        <?php } ?>
                        </tr>
                </tbody>
                <tfoot>
                    <tr>
                        <td colspan="2" class="is-size-4 has-text-right"><strong>Total</strong></td>
                        <td colspan="2" class="is-size-4">
                            $<?php echo number_format($total, 2) ?>
                        </td>
                    </tr>
                </tfoot>
            </table>
            <a href="terminar_compra.php" class="button is-success is-large"><i class="fa fa-check"></i>&nbsp;Terminar compra</a>
        </div>
    </div>
<?php } ?>
<?php include_once "pie.php" ?>

Por cierto, si el carrito está vacío se muestra otra cosa:

Carrito de compras vacío con PHP y MySQL
Carrito de compras vacío con PHP y MySQL

En el menú de navegación también se muestra la cantidad de elementos que hay en el carrito:

<strong>Ver carrito <?php
include_once "funciones.php";
$conteo = count(obtenerIdsDeProductosEnCarrito());
if ($conteo > 0) {
    printf("(%d)", $conteo);
}
?>&nbsp;<i class="fa fa-shopping-cart"></i></strong>

El conteo solo se muestra si el mismo es mayor que 0. Cuando el usuario termina la compra, solo se hace un var_dump de los productos; ahí el programador es libre de adaptar el proyecto a sus necesidades.

Poniendo todo junto

No he mostrado todo el código, solo el más relevante e importante que hacía falta explicar, pues si mostrara todo, el post sería demasiado extenso.

Te dejo el código completo en mi GitHub. También te recomiendo más posts sobre PHP y te invito a ver otros proyectos que he realizado.

Igualmente he implementado un carrito de compras usando JavaScript puro, puedes ver el ejemplo aquí.

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.

1 comentario en “Carrito de compras con PHP y MySQL”

Dejar un comentario

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