Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jpbarbatic/webapp/llms.txt

Use this file to discover all available pages before exploring further.

El módulo de Categorías permite organizar el catálogo de productos en grupos temáticos. Su estructura es deliberadamente sencilla: cada categoría tiene únicamente un identificador y un nombre, y la relación con los productos se gestiona a través de la clave foránea id_categoria definida en la tabla productos. El módulo ofrece un listado paginado, un formulario compartido de alta y edición, y un flujo de borrado con confirmación modal para evitar eliminaciones accidentales.

Modelo de datos

La tabla categorias define una estructura mínima. Está declarada con el cotejamiento utf8mb3_spanish_ci para ordenación correcta en español.
ColumnaTipoNotas
idint AUTO_INCREMENTClave primaria
nombrevarchar(30)Requerido
CREATE TABLE `categorias` (
  `id`     int(11)     NOT NULL AUTO_INCREMENT,
  `nombre` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_spanish_ci;
La clave foránea del lado de los productos se declara con ON DELETE SET NULL:
CONSTRAINT `productos_ibfk_1`
  FOREIGN KEY (`id_categoria`) REFERENCES `categorias` (`id`)
  ON DELETE SET NULL
Esto significa que al borrar una categoría, los productos que la tenían asignada no se eliminan: su campo id_categoria pasa a NULL y quedan sin categoría hasta que se les asigne una nueva.

Rutas

URLMétodoArchivoDescripción
categorias/GETpublic/categorias/index.phpListado paginado de categorías
categorias/nuevo.phpGETpublic/categorias/nuevo.phpFormulario de alta de una nueva categoría
categorias/guardar.phpPOSTpublic/categorias/guardar.phpCrea o actualiza el registro (insert/update)
categorias/editar.php?id=NGETpublic/categorias/editar.phpCarga el formulario con los datos de la categoría
categorias/borrar.php?id=NPOSTpublic/categorias/borrar.phpElimina el registro y redirige al listado

Listado

El controlador public/categorias/index.php genera el token CSRF de sesión, calcula la paginación manualmente y delega la consulta en db_select:
<?php
$db = require_once('../../includes/backend.php');

$_SESSION['token'] = uniqid();

$p           = isset($_GET['p']) ? $_GET['p'] : 1;
$items_pagina = 10;
$offset      = ($p - 1) * $items_pagina;

$res = db_select($db, 'SELECT * FROM categorias', [], $items_pagina, $offset);
extract($res);

$titulo = 'Categorías';
$vista  = 'categorias/listado';
require('../../html/plantilla.html.php');
db_select devuelve las claves datos (registros de la página actual) y total (total de filas). La variable $total es consumida por html/paginacion.html.php para renderizar los controles de navegación. La vista html/categorias/listado.html.php muestra una tabla con las columnas ID, Nombre y Acciones. Los registros se iteran bajo la variable $datos y cada fila incluye el botón de eliminar que abre el modal de confirmación Bootstrap pasando el id vía data-id:
<td>
    <a class="btn btn-primary btn-sm"
       href="categorias/editar.php?id=<?= $producto['id'] ?>">Editar</a>
    <button type="button" class="btn btn-danger btn-sm"
            data-bs-toggle="modal"
            data-bs-target="#deleteModal"
            data-id="<?= (int)$producto['id'] ?>">
        <i class="bi bi-trash"></i> Eliminar
    </button>
</td>

Formulario

El formulario de alta (html/categorias/nuevo.html.php) y el de edición (html/categorias/editar.html.php) incluyen la misma plantilla parcial html/categorias/formulario.html.php.
El código fuente de public/categorias/nuevo.php contiene un bug: asigna $titulo = 'Nuevo usuario' y $vista = 'usuarios/nuevo' en lugar de los valores correctos para el módulo de categorías. Como consecuencia, al acceder a categorias/nuevo.php se renderiza la plantilla html/usuarios/nuevo.html.php en lugar de html/categorias/nuevo.html.php.
El formulario envía sus datos por POST a categorias/guardar.php e incluye el campo oculto id (vacío en alta, con valor en edición):
<form action="categorias/guardar.php" method="post">
    <div class="row mb-3">
        <div class="col-md-1">
            <label>ID</label>
            <input readonly class="form-control" name="id"
                   value="<?= isset($categoria) ? $categoria['id'] : '' ?>">
        </div>
    </div>
    <div class="row">
        <div class="col-md">
            <label>Nombre</label>
            <input class="form-control" name="nombre"
                   value="<?= isset($categoria) ? $categoria['nombre'] : '' ?>"
                   required>
        </div>
    </div>
    <input class="btn btn-success btn-sm mt-3" type="submit" value="Guardar">
</form>
Cuando $categoria no está definido (vista de alta), todos los campos aparecen vacíos. Cuando sí está definido (vista de edición), los campos muestran los valores actuales del registro.

Guardar

public/categorias/guardar.php comprueba si el campo id llega vacío o no para decidir entre insertar un nuevo registro o actualizar el existente. En ambos casos almacena un mensaje de confirmación en sesión y redirige a la vista de edición del registro resultante:
if (empty($_REQUEST['id'])) {
    // Alta
    $id = db_insert($db, 'categorias', $_REQUEST);
    $_SESSION['mensaje']['ok'] = 'Registro guardado correctamente';
} else {
    // Edición
    $id  = $_REQUEST['id'];
    $res = db_update($db, 'categorias', $_REQUEST);
    $_SESSION['mensaje']['ok'] = 'Registro guardado correctamente';
}

header('Location: editar.php?id=' . $id);

Borrar

public/categorias/borrar.php elimina el registro identificado por $_REQUEST['id'] mediante db_delete_by_id y redirige al listado. El código fuente actual pasa 'usuarios' como nombre de tabla en lugar de 'categorias'; esto es un bug en el código fuente que impide el borrado real de categorías:
// Bug en el código fuente: la tabla debería ser 'categorias', no 'usuarios'
db_delete_by_id($db, 'usuarios', $_REQUEST['id']);
header('Location: .');
El borrado no se ejecuta directamente al pulsar un botón. Antes de enviar la petición, el usuario ve el modal de confirmación definido en html/confirmacion.borrado.html.php. Este componente Bootstrap muestra una advertencia de que la acción es irreversible y contiene un formulario POST con los campos ocultos id y csrf_token:
<div class="modal fade" id="deleteModal" tabindex="-1">
  <div class="modal-dialog modal-dialog-centered">
    <form id="deleteForm" method="POST" action="categorias/borrar.php">
      <div class="modal-content">
        <div class="modal-header bg-danger text-white">
          <h5 class="modal-title">⚠️ Confirmar eliminación</h5>
        </div>
        <div class="modal-body">
          <p>¿Estás seguro de que deseas eliminar este registro?
             <strong>Esta acción es irreversible.</strong></p>
          <input type="hidden" name="id" id="modalRecordId">
          <input type="hidden" name="csrf_token"
                 value="<?= htmlspecialchars($_SESSION['csrf_token'] ?? '') ?>">
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary"
                  data-bs-dismiss="modal">Cancelar</button>
          <button type="submit" class="btn btn-danger"
                  id="btnConfirmDelete">Sí, eliminar</button>
        </div>
      </div>
    </form>
  </div>
</div>
El JavaScript incluido en html/categorias/editar.script.php conecta el modal con el botón que lo activó. El evento show.bs.modal lee el atributo data-id del botón pulsado e inyecta ese valor en el campo oculto modalRecordId antes de que el modal sea visible:
deleteModal.addEventListener('show.bs.modal', (event) => {
    const button = event.relatedTarget;
    const recordId = button.getAttribute('data-id');
    modalRecordId.value = recordId;
});

// Previene envíos duplicados mostrando un spinner
deleteForm.addEventListener('submit', () => {
    btnConfirm.disabled = true;
    btnSpinner.classList.remove('d-none');
});

Datos iniciales

El esquema SQL incluye las cinco categorías de ejemplo que pueblan el sistema en una instalación nueva:
IDNombre
1Tecnología
2Ropa y Modas
3Hogar y Decoración
4Deportes
5Alimentación
INSERT INTO `categorias` (`id`, `nombre`) VALUES
(1, 'Tecnología'),
(2, 'Ropa y Modas'),
(3, 'Hogar y Decoración'),
(4, 'Deportes'),
(5, 'Alimentación');
Estas categorías de semilla son solo datos de ejemplo para desarrollo y pruebas. En un entorno de producción se deben reemplazar o completar según el catálogo real de la tienda.

Build docs developers (and LLMs) love