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 panel de administración utiliza MariaDB (compatible con MySQL) como motor de base de datos. El esquema completo está definido en el archivo esquema.sql, que contiene la definición de 6 tablas con sus respectivas restricciones de integridad referencial, junto con datos de ejemplo (seed data) listos para un entorno de desarrollo. El conjunto de codificaciones es utf8mb4 con la colación utf8mb4_uca1400_ai_ci en la mayoría de tablas, salvo categorias y usuarios que usan utf8mb3_spanish_ci y utf8mb4_spanish_ci respectivamente.
Diagrama de relaciones
Las tablas se relacionan entre sí de la siguiente forma:
categorias ──(1:N)──► productos
(id_categoria → ON DELETE SET NULL)
roles ──(1:N)──► usuarios
(id_rol → ON DELETE SET NULL)
roles ──(1:N)──► roles_permisos
(id_rol)
permisos ──(1:N)──► roles_permisos
(id_permiso)
| Relación | Tabla origen | Columna FK | Tabla destino | Comportamiento al borrar |
|---|
| Categoría → Productos | productos | id_categoria | categorias | SET NULL |
| Rol → Usuarios | usuarios | id_rol | roles | SET NULL |
| Rol → Roles-Permisos | roles_permisos | id_rol | roles | Restricción |
| Permiso → Roles-Permisos | roles_permisos | id_permiso | permisos | Restricción |
La política ON DELETE SET NULL en productos y usuarios evita la pérdida de datos al eliminar una categoría o un rol: los registros huérfanos quedan con el campo FK en NULL en lugar de eliminarse en cascada.
Tabla: categorias
Agrupa los productos en categorías temáticas. Es la tabla más simple del esquema.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id | int(11) | NOT NULL | AUTO_INCREMENT, PK | Identificador único de la categoría |
nombre | varchar(30) | NOT NULL | — | Nombre de la categoría (máx. 30 caracteres) |
DROP TABLE IF EXISTS `categorias`;
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;
Datos de ejemplo incluidos en el seed:
| id | nombre |
|---|
| 1 | Tecnología |
| 2 | Ropa y Modas |
| 3 | Hogar y Decoración |
| 4 | Deportes |
| 5 | Alimentación |
Tabla: productos
Almacena el catálogo de productos del panel. Incluye precio, stock, categoría y contador de fotografías.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id | int(11) | NOT NULL | AUTO_INCREMENT, PK | Identificador único del producto |
nombre | varchar(30) | NOT NULL | — | Nombre del producto (máx. 30 caracteres) |
descripcion | varchar(500) | NOT NULL | — | Descripción larga del producto (máx. 500 caracteres) |
precio | decimal(10,2) | DEFAULT NULL | — | Precio con dos decimales; NULL si no tiene precio asignado |
stock | int(11) | DEFAULT NULL | — | Unidades disponibles; NULL indica stock no controlado |
id_categoria | int(11) | DEFAULT NULL | FK → categorias.id | Categoría a la que pertenece; NULL si la categoría fue eliminada |
num_fotos | int(11) | DEFAULT 0 | — | Número de imágenes asociadas al producto |
precio: campo decimal de 10 dígitos en total con 2 decimales. Admite NULL para productos sin precio publicado.
stock: entero que puede ser NULL. Un valor 0 indica agotado; NULL indica que el stock no se gestiona para ese artículo.
id_categoria: clave foránea con ON DELETE SET NULL. Si se elimina la categoría padre, el producto no se borra: su id_categoria pasa a ser NULL.
num_fotos: contador mantenido por la aplicación que refleja cuántas fotos tiene subidas el producto. El valor por defecto es 0.
DROP TABLE IF EXISTS `productos`;
CREATE TABLE `productos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` varchar(30) NOT NULL,
`descripcion` varchar(500) NOT NULL,
`precio` decimal(10,2) DEFAULT NULL,
`stock` int(11) DEFAULT NULL,
`id_categoria` int(11) DEFAULT NULL,
`num_fotos` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
KEY `id_categoria` (`id_categoria`),
CONSTRAINT `productos_ibfk_1` FOREIGN KEY (`id_categoria`) REFERENCES `categorias` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
El seed incluye 49 productos distribuidos entre las 5 categorías, con precios y stocks variados, incluyendo algunos registros con stock NULL.
Tabla: usuarios
Almacena las cuentas de acceso al panel. Cada usuario tiene exactamente un rol asignado.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id | int(11) | NOT NULL | AUTO_INCREMENT, PK | Identificador único del usuario |
nombre | char(20) | NOT NULL | — | Nombre de pila (máx. 20 caracteres) |
apellidos | char(30) | NOT NULL | — | Apellidos (máx. 30 caracteres) |
email | char(50) | NOT NULL | — | Dirección de correo electrónico; se usa como credencial de login |
telefono | char(12) | DEFAULT NULL | — | Número de teléfono opcional (máx. 12 caracteres, admite prefijo +34) |
password | char(60) | NOT NULL | DEFAULT '' | Hash bcrypt de la contraseña |
id_rol | int(11) | DEFAULT NULL | FK → roles.id | Rol del usuario; NULL si el rol fue eliminado |
El campo password almacena el hash generado por la función password_hash() de PHP con el algoritmo PASSWORD_BCRYPT. El resultado siempre ocupa exactamente 60 caracteres, de ahí que el tipo sea char(60). Nunca se almacena la contraseña en texto plano.
DROP TABLE IF EXISTS `usuarios`;
CREATE TABLE `usuarios` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` char(20) NOT NULL,
`apellidos` char(30) NOT NULL,
`email` char(50) NOT NULL,
`telefono` char(12) DEFAULT NULL,
`password` char(60) NOT NULL DEFAULT '',
`id_rol` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_rol` (`id_rol`),
CONSTRAINT `usuarios_ibfk_2` FOREIGN KEY (`id_rol`) REFERENCES `roles` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci;
El seed incluye un usuario administrador con email admin@example.com y contraseña hasheada con bcrypt, más 43 usuarios de prueba adicionales, todos con id_rol = 1 (administrador).
Tabla: roles
Define los roles disponibles en el sistema. Cada rol puede tener un conjunto de permisos asignados a través de la tabla pivot roles_permisos.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id | int(11) | NOT NULL | AUTO_INCREMENT, PK | Identificador único del rol |
nombre | char(50) | NOT NULL | — | Nombre del rol (p. ej. admin, editor) |
descripcion | char(100) | NOT NULL | — | Descripción legible del rol |
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` char(50) NOT NULL,
`descripcion` char(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
El seed incluye un único rol inicial:
| id | nombre | descripcion |
|---|
| 1 | admin | Administrador |
Tabla: permisos
Contiene el catálogo de permisos atómicos del sistema. Los permisos pueden tener dependencias entre sí a través del campo depende_de.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id | int(11) | NOT NULL | AUTO_INCREMENT, PK | Identificador único del permiso |
nombre | char(50) | NOT NULL | — | Nombre del permiso en formato recurso.accion |
descripcion | char(100) | NOT NULL | — | Descripción del permiso (puede estar vacía) |
depende_de | int(11) | NOT NULL | — | ID del permiso del que depende; 0 si no tiene dependencia |
El campo depende_de permite modelar jerarquías de permisos. Por ejemplo, los permisos usuarios.borrar y usuarios.actualizar dependen de usuarios.consulta (id 1): no tiene sentido borrar o actualizar si no se puede listar. Un valor de 0 indica que el permiso no tiene dependencia.
DROP TABLE IF EXISTS `permisos`;
CREATE TABLE `permisos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` char(50) NOT NULL,
`descripcion` char(100) NOT NULL,
`depende_de` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
Permisos de ejemplo incluidos en el seed:
| id | nombre | descripcion | depende_de |
|---|
| 1 | usuarios.consulta | (vacío) | 0 (sin dependencia) |
| 2 | usuarios.crear | (vacío) | 0 (sin dependencia) |
| 3 | usuarios.borrar | (vacío) | 1 (usuarios.consulta) |
| 4 | usuarios.actualizar | (vacío) | 1 (usuarios.consulta) |
Tabla: roles_permisos
Tabla pivot que relaciona roles con permisos en una relación muchos-a-muchos. No tiene clave primaria propia: la combinación de id_rol e id_permiso identifica cada asignación.
| Columna | Tipo | Nulidad | Extra | Descripción |
|---|
id_rol | int(11) | NOT NULL | FK → roles.id | Identificador del rol |
id_permiso | int(11) | NOT NULL | FK → permisos.id | Identificador del permiso |
DROP TABLE IF EXISTS `roles_permisos`;
CREATE TABLE `roles_permisos` (
`id_rol` int(11) NOT NULL,
`id_permiso` int(11) NOT NULL,
KEY `id_rol` (`id_rol`),
KEY `id_permiso` (`id_permiso`),
CONSTRAINT `roles_permisos_ibfk_1` FOREIGN KEY (`id_rol`) REFERENCES `roles` (`id`),
CONSTRAINT `roles_permisos_ibfk_2` FOREIGN KEY (`id_permiso`) REFERENCES `permisos` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
El seed asigna al rol admin (id 1) los permisos usuarios.consulta (id 1) y usuarios.crear (id 2).
Carga del esquema
Existen dos métodos para importar el esquema en una base de datos nueva.
Método 1: cliente MySQL por línea de comandos
Crear la base de datos
mysql -u root -p -e "CREATE DATABASE empresa CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;"
Importar el esquema
mysql -u root -p empresa < esquema.sql
Verificar las tablas
mysql -u root -p empresa -e "SHOW TABLES;"
Deberían aparecer las 6 tablas: categorias, permisos, productos, roles, roles_permisos, usuarios.
Método 2: utilidad web bd.php
El proyecto incluye el archivo bd.php en la raíz. Al abrirlo en el navegador muestra un formulario con los datos de conexión (leídos desde config.php) y los archivos .sql disponibles en el directorio raíz. Basta con seleccionar esquema.sql y pulsar Cargar Fichero.
El script detecta automáticamente todos los archivos .sql presentes en la raíz (glob("*.sql")), divide el contenido por ; y ejecuta cada sentencia individualmente con $pdo->exec().
bd.php no requiere autenticación. Cualquier persona con acceso HTTP a la URL puede ejecutar cualquier archivo SQL del directorio raíz, incluyendo operaciones destructivas como DROP TABLE. Elimina o protege este archivo antes de desplegar en producción. Como mínimo, restringe el acceso mediante autenticación HTTP básica en el servidor web o elimina el archivo una vez realizada la carga inicial del esquema.