BD CLASE 4 2024-II

 


Conectar una base de datos a una aplicación web desde cero modificando plantillas HTML para las diferentes páginas del sitio (inicio, registro, login, etc.).

Escribir consultas SQL para realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) en la base de datos.

Integración del Frontend y Backend

• Enviar datos desde el frontend al backend utilizando el método GET o POST.

• Recibir los datos en el backend y procesarlos.

• Redirigir al usuario a la página correspondiente según la acción realizada.

• Mostrar mensajes de error o éxito al usuario.

•  Modificación de registros:

• Permitir al usuario editar sus propios datos.

• Implementar controles de acceso para evitar que el usuario modifique datos que no le pertenecen.

•  Asignación de registros:

• Establecer relaciones entre registros de diferentes tablas.

• Implementar reglas de negocio para las asignaciones.

•  Consultas avanzadas:

• Realizar consultas que involucren múltiples tablas y condiciones complejas.

• Mostrar resultados de consultas en tablas HTML y gestionar la seguridad de los usuarios por medio de variables de sesión.


Teniendo en cuenta los objetivos detallados, este tutorial te guiará en la construcción de una aplicación web básica desde cero, abarcando la conexión a la base de datos, operaciones CRUD, integración Frontend-Backend, gestión de sesiones y funcionalidades avanzadas como la modificación y asignación de registros. Se asumirá el uso de PHP y MySQL con XAMPP como entorno de desarrollo.


Tutorial: Desarrollo de Aplicaciones Web con Base de Datos Relacional (PHP y MySQL)

Este tutorial aborda la creación de una aplicación web dinámica, conectando el frontend con un backend basado en PHP y MySQL para gestionar datos. Se cubrirán desde la estructura básica del proyecto hasta operaciones avanzadas y gestión de sesiones.

Requisitos Previos:

  • XAMPP instalado y funcionando: Asegúrate de que Apache y MySQL estén iniciados en tu Panel de Control de XAMPP.
  • Conocimientos básicos de HTML, CSS y PHP.
  • Editor de código: Visual Studio Code, Sublime Text, Notepad++, etc.

Estructura del Proyecto

Crea una carpeta llamada mi_aplicacion_web dentro de C:\xampp\htdocs\. Dentro de esta, organiza los archivos de la siguiente manera:

mi_aplicacion_web/
├── css/
│   └── style.css
├── includes/
│   └── conexion.php
│   └── funciones.php
├── index.php           (Página de inicio, sin login)
├── registro.php        (Formulario y lógica de registro)
├── login.php           (Formulario y lógica de login)
├── dashboard.php       (Página principal para usuarios logueados)
├── vehiculos.php       (CRUD de vehículos)
├── empleados.php       (CRUD de empleados)
├── mantenimientos.php  (CRUD de mantenimientos, incluye asignación)
├── perfil.php          (Modificación de perfil del usuario)
└── logout.php          (Cierre de sesión)

Parte 1: Base de Datos (Reutilizando el Diseño de Vehículos y Mantenimientos)

Utilizaremos la base de datos db_vehiculos_mantenimiento y sus tablas (Vehiculos, Empleados, Mantenimientos, Asignaciones_Empleado_Vehiculo) que creamos en el tutorial anterior. Si aún no las tienes, créalas en phpMyAdmin siguiendo los pasos del tutorial "Diseño y Creación de una Base de Datos Relacional para Gestión de Vehículos y Mantenimientos".

Tabla Adicional para Usuarios (para Login y Registro):

Necesitamos una tabla Usuarios para gestionar el acceso a la aplicación.

SQL
CREATE TABLE Usuarios (
    id_usuario INT AUTO_INCREMENT PRIMARY KEY,
    nombre_usuario VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    contrasena_hash VARCHAR(255) NOT NULL, -- Almacenar contraseñas hasheadas
    rol VARCHAR(20) DEFAULT 'usuario' -- Ej. 'administrador', 'usuario'
);

Parte 2: Conexión y Funciones de Utilidad (Backend)

  1. includes/conexion.php: Archivo para establecer la conexión a la base de datos.

    PHP
    <?php
    $servername = "localhost";
    $username = "root";
    $password = "";
    $dbname = "db_vehiculos_mantenimiento"; // Reemplaza con el nombre de tu BD
    
    // Crear conexión
    $conn = new mysqli($servername, $username, $password, $dbname);
    
    // Verificar conexión
    if ($conn->connect_error) {
        die("Conexión fallida: " . $conn->connect_error);
    }
    // Opcional: Establecer el juego de caracteres a UTF-8
    $conn->set_charset("utf8mb4");
    ?>
    
  2. includes/funciones.php: Archivo para funciones de utilidad (ej. redirecciones, sanitización de datos).

    PHP
    <?php
    // Función para sanear datos de entrada
    function sanear_dato($conn, $dato) {
        return $conn->real_escape_string(htmlspecialchars(strip_tags($dato)));
    }
    
    // Función para redirigir
    function redireccionar($url) {
        header("Location: " . $url);
        exit();
    }
    
    // Función para mostrar mensajes (usando variables de sesión)
    function mostrar_mensaje() {
        if (isset($_SESSION['mensaje'])) {
            echo "<div class='mensaje'>" . $_SESSION['mensaje'] . "</div>";
            unset($_SESSION['mensaje']); // Limpiar mensaje después de mostrar
        }
        if (isset($_SESSION['error'])) {
            echo "<div class='error'>" . $_SESSION['error'] . "</div>";
            unset($_SESSION['error']); // Limpiar error después de mostrar
        }
    }
    
    // Función para verificar si el usuario está logueado
    function is_logged_in() {
        return isset($_SESSION['id_usuario']);
    }
    
    // Función para verificar rol (ej. 'administrador')
    function check_role($role) {
        return is_logged_in() && $_SESSION['rol'] === $role;
    }
    ?>
    
  3. css/style.css: Un CSS básico para dar estilo a las páginas.

    CSS
    body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }
    .container { max-width: 800px; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
    h1, h2 { color: #333; }
    form { display: flex; flex-direction: column; gap: 10px; }
    label { font-weight: bold; }
    input[type="text"], input[type="password"], input[type="email"], input[type="date"], input[type="number"], select, textarea {
        padding: 8px; border: 1px solid #ddd; border-radius: 4px;
    }
    input[type="submit"], button {
        background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px;
        cursor: pointer; font-size: 16px;
    }
    input[type="submit"]:hover, button:hover { background-color: #0056b3; }
    .mensaje { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; padding: 10px; margin-bottom: 15px; border-radius: 4px; }
    .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; padding: 10px; margin-bottom: 15px; border-radius: 4px; }
    table { width: 100%; border-collapse: collapse; margin-top: 20px; }
    table, th, td { border: 1px solid #ddd; }
    th, td { padding: 8px; text-align: left; }
    th { background-color: #f2f2f2; }
    .actions a { margin-right: 10px; text-decoration: none; color: #007bff; }
    .actions a:hover { text-decoration: underline; }
    .navbar { background-color: #333; overflow: hidden; }
    .navbar a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; }
    .navbar a:hover { background-color: #ddd; color: black; }
    .navbar .right { float: right; }
    

Parte 3: Páginas del Sitio (Frontend y Backend)

Todas las páginas PHP deben comenzar con session_start(); para habilitar las variables de sesión y el include de conexion.php y funciones.php.

1. index.php (Página de Inicio)

Muestra un mensaje de bienvenida y enlaces a registro/login.

PHP
<?php
session_start();
include 'includes/conexion.php';
include 'includes/funciones.php';
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Inicio - Mi Aplicación Web</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="navbar">
        <a href="index.php">Inicio</a>
        <?php if (!is_logged_in()): ?>
            <div class="right">
                <a href="registro.php">Registro</a>
                <a href="login.php">Login</a>
            </div>
        <?php else: ?>
            <div class="right">
                <a href="dashboard.php">Dashboard</a>
                <a href="perfil.php">Mi Perfil</a>
                <a href="logout.php">Cerrar Sesión</a>
            </div>
        <?php endif; ?>
    </div>
    <div class="container">
        <h1>Bienvenido a la Aplicación de Gestión de Vehículos y Mantenimientos</h1>
        <?php mostrar_mensaje(); ?>
        <p>Esta aplicación te permite gestionar tus vehículos, empleados y mantenimientos.</p>
        <?php if (!is_logged_in()): ?>
            <p>Por favor, <a href="login.php">inicia sesión</a> o <a href="registro.php">regístrate</a> para continuar.</p>
        <?php endif; ?>
    </div>
</body>
</html>

2. registro.php (Registro de Usuarios)

  • Frontend (Formulario):

    HTML
    <h2>Registro de Usuario</h2>
    <form action="registro.php" method="POST">
        <label for="nombre_usuario">Nombre de Usuario:</label>
        <input type="text" id="nombre_usuario" name="nombre_usuario" required>
    
        <label for="email">Email:</label>
        <input type="email" id="email" name="email" required>
    
        <label for="contrasena">Contraseña:</label>
        <input type="password" id="contrasena" name="contrasena" required>
    
        <label for="confirmar_contrasena">Confirmar Contraseña:</label>
        <input type="password" id="confirmar_contrasena" name="confirmar_contrasena" required>
    
        <input type="submit" value="Registrarse">
    </form>
    <p>¿Ya tienes una cuenta? <a href="login.php">Inicia Sesión</a></p>
    
  • Backend (Lógica PHP para procesar POST):

    PHP
    <?php
    session_start();
    include 'includes/conexion.php';
    include 'includes/funciones.php';
    
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $nombre_usuario = sanear_dato($conn, $_POST['nombre_usuario']);
        $email = sanear_dato($conn, $_POST['email']);
        $contrasena = $_POST['contrasena'];
        $confirmar_contrasena = $_POST['confirmar_contrasena'];
    
        if (empty($nombre_usuario) || empty($email) || empty($contrasena) || empty($confirmar_contrasena)) {
            $_SESSION['error'] = "Todos los campos son obligatorios.";
        } elseif ($contrasena !== $confirmar_contrasena) {
            $_SESSION['error'] = "Las contraseñas no coinciden.";
        } elseif (strlen($contrasena) < 6) {
            $_SESSION['error'] = "La contraseña debe tener al menos 6 caracteres.";
        } else {
            // Hash de la contraseña antes de guardar
            $contrasena_hash = password_hash($contrasena, PASSWORD_DEFAULT);
    
            // Verificar si el usuario o email ya existen
            $stmt = $conn->prepare("SELECT id_usuario FROM Usuarios WHERE nombre_usuario = ? OR email = ?");
            $stmt->bind_param("ss", $nombre_usuario, $email);
            $stmt->execute();
            $stmt->store_result();
    
            if ($stmt->num_rows > 0) {
                $_SESSION['error'] = "El nombre de usuario o email ya está en uso.";
            } else {
                // Insertar nuevo usuario
                $stmt = $conn->prepare("INSERT INTO Usuarios (nombre_usuario, email, contrasena_hash) VALUES (?, ?, ?)");
                $stmt->bind_param("sss", $nombre_usuario, $email, $contrasena_hash);
    
                if ($stmt->execute()) {
                    $_SESSION['mensaje'] = "Registro exitoso. Ahora puedes iniciar sesión.";
                    redireccionar("login.php");
                } else {
                    $_SESSION['error'] = "Error al registrar usuario: " . $stmt->error;
                }
            }
            $stmt->close();
        }
    }
    // Incluir la plantilla HTML del formulario de registro
    // ... (El resto del HTML del formulario de registro como en index.php)
    ?>
    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Registro - Mi Aplicación Web</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div class="navbar">
            <a href="index.php">Inicio</a>
            <div class="right">
                <a href="registro.php">Registro</a>
                <a href="login.php">Login</a>
            </div>
        </div>
        <div class="container">
            <?php mostrar_mensaje(); ?>
            <h2>Registro de Usuario</h2>
            <form action="registro.php" method="POST">
                <label for="nombre_usuario">Nombre de Usuario:</label>
                <input type="text" id="nombre_usuario" name="nombre_usuario" required>
    
                <label for="email">Email:</label>
                <input type="email" id="email" name="email" required>
    
                <label for="contrasena">Contraseña:</label>
                <input type="password" id="contrasena" name="contrasena" required>
    
                <label for="confirmar_contrasena">Confirmar Contraseña:</label>
                <input type="password" id="confirmar_contrasena" name="confirmar_contrasena" required>
    
                <input type="submit" value="Registrarse">
            </form>
            <p>¿Ya tienes una cuenta? <a href="login.php">Inicia Sesión</a></p>
        </div>
    </body>
    </html>
    

3. login.php (Inicio de Sesión)

  • Frontend (Formulario):

    HTML
    <h2>Inicio de Sesión</h2>
    <form action="login.php" method="POST">
        <label for="nombre_usuario">Nombre de Usuario o Email:</label>
        <input type="text" id="nombre_usuario" name="nombre_usuario" required>
    
        <label for="contrasena">Contraseña:</label>
        <input type="password" id="contrasena" name="contrasena" required>
    
        <input type="submit" value="Iniciar Sesión">
    </form>
    <p>¿No tienes una cuenta? <a href="registro.php">Regístrate</a></p>
    
  • Backend (Lógica PHP para procesar POST y gestionar sesión):

    PHP
    <?php
    session_start();
    include 'includes/conexion.php';
    include 'includes/funciones.php';
    
    if (is_logged_in()) {
        redireccionar("dashboard.php"); // Si ya está logueado, redirigir al dashboard
    }
    
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $nombre_usuario_o_email = sanear_dato($conn, $_POST['nombre_usuario']);
        $contrasena = $_POST['contrasena'];
    
        if (empty($nombre_usuario_o_email) || empty($contrasena)) {
            $_SESSION['error'] = "Todos los campos son obligatorios.";
        } else {
            // Preparar la consulta para buscar por nombre de usuario o email
            $stmt = $conn->prepare("SELECT id_usuario, nombre_usuario, contrasena_hash, rol FROM Usuarios WHERE nombre_usuario = ? OR email = ?");
            $stmt->bind_param("ss", $nombre_usuario_o_email, $nombre_usuario_o_email);
            $stmt->execute();
            $result = $stmt->get_result();
    
            if ($result->num_rows == 1) {
                $usuario = $result->fetch_assoc();
                // Verificar la contraseña hasheada
                if (password_verify($contrasena, $usuario['contrasena_hash'])) {
                    // Contraseña correcta, iniciar sesión
                    $_SESSION['id_usuario'] = $usuario['id_usuario'];
                    $_SESSION['nombre_usuario'] = $usuario['nombre_usuario'];
                    $_SESSION['rol'] = $usuario['rol'];
                    $_SESSION['mensaje'] = "¡Bienvenido, " . $usuario['nombre_usuario'] . "!";
                    redireccionar("dashboard.php");
                } else {
                    $_SESSION['error'] = "Nombre de usuario o contraseña incorrectos.";
                }
            } else {
                $_SESSION['error'] = "Nombre de usuario o contraseña incorrectos.";
            }
            $stmt->close();
        }
    }
    // Incluir la plantilla HTML del formulario de login
    // ... (El resto del HTML del formulario de login como en index.php)
    ?>
    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Login - Mi Aplicación Web</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div class="navbar">
            <a href="index.php">Inicio</a>
            <div class="right">
                <a href="registro.php">Registro</a>
                <a href="login.php">Login</a>
            </div>
        </div>
        <div class="container">
            <?php mostrar_mensaje(); ?>
            <?php mostrar_error(); ?>
            <h2>Inicio de Sesión</h2>
            <form action="login.php" method="POST">
                <label for="nombre_usuario">Nombre de Usuario o Email:</label>
                <input type="text" id="nombre_usuario" name="nombre_usuario" required>
    
                <label for="contrasena">Contraseña:</label>
                <input type="password" id="contrasena" name="contrasena" required>
    
                <input type="submit" value="Iniciar Sesión">
            </form>
            <p>¿No tienes una cuenta? <a href="registro.php">Regístrate</a></p>
        </div>
    </body>
    </html>
    

4. dashboard.php (Página de Dashboard)

Acceso restringido solo a usuarios logueados.

PHP
<?php
session_start();
include 'includes/conexion.php';
include 'includes/funciones.php';

// Redirigir si no está logueado
if (!is_logged_in()) {
    $_SESSION['error'] = "Debes iniciar sesión para acceder a esta página.";
    redireccionar("login.php");
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard - Mi Aplicación Web</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="navbar">
        <a href="index.php">Inicio</a>
        <a href="dashboard.php">Dashboard</a>
        <a href="vehiculos.php">Vehículos</a>
        <a href="empleados.php">Empleados</a>
        <a href="mantenimientos.php">Mantenimientos</a>
        <div class="right">
            <a href="perfil.php">Mi Perfil (<?php echo htmlspecialchars($_SESSION['nombre_usuario']); ?>)</a>
            <a href="logout.php">Cerrar Sesión</a>
        </div>
    </div>
    <div class="container">
        <h1>Dashboard</h1>
        <?php mostrar_mensaje(); ?>
        <p>Hola, <?php echo htmlspecialchars($_SESSION['nombre_usuario']); ?>. ¡Bienvenido a tu panel de control!</p>
        <p>Desde aquí puedes gestionar:</p>
        <ul>
            <li><a href="vehiculos.php">Vehículos</a></li>
            <li><a href="empleados.php">Empleados</a></li>
            <li><a href="mantenimientos.php">Mantenimientos</a></li>
        </ul>
        <?php if (check_role('administrador')): ?>
            <p>Como **Administrador** tienes acceso a todas las funcionalidades.</p>
        <?php endif; ?>
    </div>
</body>
</html>

5. logout.php (Cierre de Sesión)

PHP
<?php
session_start();
session_unset(); // Eliminar todas las variables de sesión
session_destroy(); // Destruir la sesión
include 'includes/funciones.php';
$_SESSION['mensaje'] = "Has cerrado sesión exitosamente.";
redireccionar("login.php");
?>

6. perfil.php (Modificación de Registros - Perfil de Usuario)

Permite al usuario editar su propio nombre de usuario y email.

PHP
<?php
session_start();
include 'includes/conexion.php';
include 'includes/funciones.php';

if (!is_logged_in()) {
    $_SESSION['error'] = "Debes iniciar sesión para acceder a esta página.";
    redireccionar("login.php");
}

$id_usuario = $_SESSION['id_usuario'];
$current_nombre_usuario = $_SESSION['nombre_usuario'];
$current_email = '';
$current_rol = $_SESSION['rol']; // Para mostrar su rol, pero no se puede editar directamente

// Cargar datos actuales del usuario
$stmt = $conn->prepare("SELECT email FROM Usuarios WHERE id_usuario = ?");
$stmt->bind_param("i", $id_usuario);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 1) {
    $row = $result->fetch_assoc();
    $current_email = $row['email'];
}
$stmt->close();

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $nuevo_nombre_usuario = sanear_dato($conn, $_POST['nombre_usuario']);
    $nuevo_email = sanear_dato($conn, $_POST['email']);
    $contrasena_actual = $_POST['contrasena_actual'];
    $nueva_contrasena = $_POST['nueva_contrasena'];
    $confirmar_nueva_contrasena = $_POST['confirmar_nueva_contrasena'];

    // Validaciones
    $errores = [];
    if (empty($nuevo_nombre_usuario) || empty($nuevo_email)) {
        $errores[] = "El nombre de usuario y el email no pueden estar vacíos.";
    }
    if ($nuevo_nombre_usuario !== $current_nombre_usuario) {
        // Verificar si el nuevo nombre de usuario ya existe
        $stmt_check = $conn->prepare("SELECT id_usuario FROM Usuarios WHERE nombre_usuario = ? AND id_usuario != ?");
        $stmt_check->bind_param("si", $nuevo_nombre_usuario, $id_usuario);
        $stmt_check->execute();
        $stmt_check->store_result();
        if ($stmt_check->num_rows > 0) {
            $errores[] = "El nuevo nombre de usuario ya está en uso.";
        }
        $stmt_check->close();
    }
    if ($nuevo_email !== $current_email) {
        // Verificar si el nuevo email ya existe
        $stmt_check = $conn->prepare("SELECT id_usuario FROM Usuarios WHERE email = ? AND id_usuario != ?");
        $stmt_check->bind_param("si", $nuevo_email, $id_usuario);
        $stmt_check->execute();
        $stmt_check->store_result();
        if ($stmt_check->num_rows > 0) {
            $errores[] = "El nuevo email ya está en uso.";
        }
        $stmt_check->close();
    }

    // Lógica para cambiar contraseña
    if (!empty($nueva_contrasena)) {
        if (empty($contrasena_actual)) {
            $errores[] = "Debes ingresar tu contraseña actual para cambiarla.";
        } else {
            // Verificar contraseña actual
            $stmt_check_pass = $conn->prepare("SELECT contrasena_hash FROM Usuarios WHERE id_usuario = ?");
            $stmt_check_pass->bind_param("i", $id_usuario);
            $stmt_check_pass->execute();
            $result_pass = $stmt_check_pass->get_result();
            $user_pass = $result_pass->fetch_assoc();
            $stmt_check_pass->close();

            if (!password_verify($contrasena_actual, $user_pass['contrasena_hash'])) {
                $errores[] = "La contraseña actual es incorrecta.";
            } elseif ($nueva_contrasena !== $confirmar_nueva_contrasena) {
                $errores[] = "Las nuevas contraseñas no coinciden.";
            } elseif (strlen($nueva_contrasena) < 6) {
                $errores[] = "La nueva contraseña debe tener al menos 6 caracteres.";
            }
        }
    }

    if (count($errores) > 0) {
        $_SESSION['error'] = implode("<br>", $errores);
    } else {
        // Actualizar datos del usuario
        $sql_update = "UPDATE Usuarios SET nombre_usuario = ?, email = ?";
        $params = [$nuevo_nombre_usuario, $nuevo_email];
        $types = "ss";

        if (!empty($nueva_contrasena)) {
            $nueva_contrasena_hash = password_hash($nueva_contrasena, PASSWORD_DEFAULT);
            $sql_update .= ", contrasena_hash = ?";
            $params[] = $nueva_contrasena_hash;
            $types .= "s";
        }
        $sql_update .= " WHERE id_usuario = ?";
        $params[] = $id_usuario;
        $types .= "i";

        $stmt = $conn->prepare($sql_update);
        $stmt->bind_param($types, ...$params);

        if ($stmt->execute()) {
            $_SESSION['nombre_usuario'] = $nuevo_nombre_usuario; // Actualizar la sesión
            $_SESSION['mensaje'] = "Perfil actualizado exitosamente.";
            redireccionar("perfil.php");
        } else {
            $_SESSION['error'] = "Error al actualizar perfil: " . $stmt->error;
        }
        $stmt->close();
    }
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mi Perfil - Mi Aplicación Web</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="navbar">
        <a href="index.php">Inicio</a>
        <a href="dashboard.php">Dashboard</a>
        <a href="vehiculos.php">Vehículos</a>
        <a href="empleados.php">Empleados</a>
        <a href="mantenimientos.php">Mantenimientos</a>
        <div class="right">
            <a href="perfil.php">Mi Perfil (<?php echo htmlspecialchars($_SESSION['nombre_usuario']); ?>)</a>
            <a href="logout.php">Cerrar Sesión</a>
        </div>
    </div>
    <div class="container">
        <h1>Mi Perfil</h1>
        <?php mostrar_mensaje(); ?>
        <?php mostrar_error(); ?>
        <form action="perfil.php" method="POST">
            <label for="nombre_usuario">Nombre de Usuario:</label>
            <input type="text" id="nombre_usuario" name="nombre_usuario" value="<?php echo htmlspecialchars($current_nombre_usuario); ?>" required>

            <label for="email">Email:</label>
            <input type="email" id="email" name="email" value="<?php echo htmlspecialchars($current_email); ?>" required>

            <p>Tu Rol: <strong><?php echo htmlspecialchars($current_rol); ?></strong></p>

            <h3>Cambiar Contraseña (opcional)</h3>
            <label for="contrasena_actual">Contraseña Actual:</label>
            <input type="password" id="contrasena_actual" name="contrasena_actual">

            <label for="nueva_contrasena">Nueva Contraseña:</label>
            <input type="password" id="nueva_contrasena" name="nueva_contrasena">

            <label for="confirmar_nueva_contrasena">Confirmar Nueva Contraseña:</label>
            <input type="password" id="confirmar_nueva_contrasena" name="confirmar_nueva_contrasena">

            <input type="submit" value="Actualizar Perfil">
        </form>
    </div>
</body>
</html>

7. vehiculos.php (CRUD de Vehículos)

Este archivo manejará la creación, lectura, actualización y eliminación de vehículos.

  • Lógica Principal (vehiculos.php):
    PHP
    <?php
    session_start();
    include 'includes/conexion.php';
    include 'includes/funciones.php';
    
    if (!is_logged_in()) {
        $_SESSION['error'] = "Debes iniciar sesión para acceder a esta página.";
        redireccionar("login.php");
    }
    
    // Si es un usuario normal, solo puede ver/modificar sus vehículos (si tuvieras una tabla de propietarios vinculada)
    // Para simplificar, asumiremos que todos los logueados pueden ver, y solo 'administrador' puede CRUD completo
    if (!check_role('administrador')) {
        // Por ahora, permitimos que cualquier usuario vea. Para edición/eliminación, se podría restringir.
        // $_SESSION['error'] = "No tienes permiso para gestionar vehículos.";
        // redireccionar("dashboard.php");
    }
    
    $accion = $_GET['accion'] ?? 'listar'; // Default a listar
    $id = $_GET['id'] ?? null;
    
    // --- Lógica CRUD ---
    
    // Crear/Editar Vehículo
    if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['form_vehiculo'])) {
        $id_vehiculo = sanear_dato($conn, $_POST['id_vehiculo']); // Podría ser vacío si es nuevo
        $marca = sanear_dato($conn, $_POST['marca']);
        $modelo = sanear_dato($conn, $_POST['modelo']);
        $año = sanear_dato($conn, $_POST['año']);
        $placa = sanear_dato($conn, $_POST['placa']);
        $color = sanear_dato($conn, $_POST['color']);
        $numero_motor = sanear_dato($conn, $_POST['numero_motor']);
        $tipo_combustible = sanear_dato($conn, $_POST['tipo_combustible']);
        $propietario_nombre = sanear_dato($conn, $_POST['propietario_nombre']);
    
        if (empty($marca) || empty($modelo) || empty($placa)) {
            $_SESSION['error'] = "Marca, Modelo y Placa son obligatorios.";
            redireccionar("vehiculos.php?accion=crear"); // Redirige de vuelta al formulario
        } else {
            if (empty($id_vehiculo)) { // CREAR
                $stmt = $conn->prepare("INSERT INTO Vehiculos (marca, modelo, año, placa, color, numero_motor, tipo_combustible, propietario_nombre) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
                $stmt->bind_param("ssisssss", $marca, $modelo, $año, $placa, $color, $numero_motor, $tipo_combustible, $propietario_nombre);
                if ($stmt->execute()) {
                    $_SESSION['mensaje'] = "Vehículo agregado exitosamente.";
                } else {
                    $_SESSION['error'] = "Error al agregar vehículo: " . $stmt->error;
                }
            } else { // ACTUALIZAR
                // Control de acceso: Solo admin puede modificar (implementar aquí)
                if (!check_role('administrador')) {
                    $_SESSION['error'] = "No tienes permiso para modificar este vehículo.";
                    redireccionar("vehiculos.php");
                }
    
                $stmt = $conn->prepare("UPDATE Vehiculos SET marca=?, modelo=?, año=?, placa=?, color=?, numero_motor=?, tipo_combustible=?, propietario_nombre=? WHERE id_vehiculo=?");
                $stmt->bind_param("ssisssssi", $marca, $modelo, $año, $placa, $color, $numero_motor, $tipo_combustible, $propietario_nombre, $id_vehiculo);
                if ($stmt->execute()) {
                    $_SESSION['mensaje'] = "Vehículo actualizado exitosamente.";
                } else {
                    $_SESSION['error'] = "Error al actualizar vehículo: " . $stmt->error;
                }
            }
            $stmt->close();
            redireccionar("vehiculos.php");
        }
    }
    
    // Eliminar Vehículo
    if ($accion == 'eliminar' && $id && check_role('administrador')) {
        $stmt = $conn->prepare("DELETE FROM Vehiculos WHERE id_vehiculo = ?");
        $stmt->bind_param("i", $id);
        if ($stmt->execute()) {
            $_SESSION['mensaje'] = "Vehículo eliminado exitosamente.";
        } else {
            $_SESSION['error'] = "Error al eliminar vehículo: " . $stmt->error;
        }
        $stmt->close();
        redireccionar("vehiculos.php");
    } elseif ($accion == 'eliminar' && !check_role('administrador')) {
         $_SESSION['error'] = "No tienes permiso para eliminar vehículos.";
         redireccionar("vehiculos.php");
    }
    
    // --- HTML y Vistas ---
    ?>
    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Gestión de Vehículos</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div class="navbar">
            <a href="index.php">Inicio</a>
            <a href="dashboard.php">Dashboard</a>
            <a href="vehiculos.php">Vehículos</a>
            <a href="empleados.php">Empleados</a>
            <a href="mantenimientos.php">Mantenimientos</a>
            <div class="right">
                <a href="perfil.php">Mi Perfil (<?php echo htmlspecialchars($_SESSION['nombre_usuario']); ?>)</a>
                <a href="logout.php">Cerrar Sesión</a>
            </div>
        </div>
        <div class="container">
            <h1>Gestión de Vehículos</h1>
            <?php mostrar_mensaje(); ?>
            <?php mostrar_error(); ?>
    
            <?php if ($accion == 'crear' || $accion == 'editar'):
                $vehiculo = ['id_vehiculo' => '', 'marca' => '', 'modelo' => '', 'año' => '', 'placa' => '', 'color' => '', 'numero_motor' => '', 'tipo_combustible' => '', 'propietario_nombre' => ''];
                if ($accion == 'editar' && $id) {
                    $stmt = $conn->prepare("SELECT * FROM Vehiculos WHERE id_vehiculo = ?");
                    $stmt->bind_param("i", $id);
                    $stmt->execute();
                    $result = $stmt->get_result();
                    if ($result->num_rows == 1) {
                        $vehiculo = $result->fetch_assoc();
                    } else {
                        $_SESSION['error'] = "Vehículo no encontrado.";
                        redireccionar("vehiculos.php");
                    }
                    $stmt->close();
                }
            ?>
                <h2><?php echo ($accion == 'crear' ? 'Agregar Nuevo' : 'Editar'); ?> Vehículo</h2>
                <form action="vehiculos.php" method="POST">
                    <input type="hidden" name="form_vehiculo" value="1">
                    <input type="hidden" name="id_vehiculo" value="<?php echo htmlspecialchars($vehiculo['id_vehiculo']); ?>">
    
                    <label for="marca">Marca:</label>
                    <input type="text" id="marca" name="marca" value="<?php echo htmlspecialchars($vehiculo['marca']); ?>" required>
    
                    <label for="modelo">Modelo:</label>
                    <input type="text" id="modelo" name="modelo" value="<?php echo htmlspecialchars($vehiculo['modelo']); ?>" required>
    
                    <label for="año">Año:</label>
                    <input type="number" id="año" name="año" value="<?php echo htmlspecialchars($vehiculo['año']); ?>">
    
                    <label for="placa">Placa:</label>
                    <input type="text" id="placa" name="placa" value="<?php echo htmlspecialchars($vehiculo['placa']); ?>" required>
    
                    <label for="color">Color:</label>
                    <input type="text" id="color" name="color" value="<?php echo htmlspecialchars($vehiculo['color']); ?>">
    
                    <label for="numero_motor">Número de Motor:</label>
                    <input type="text" id="numero_motor" name="numero_motor" value="<?php echo htmlspecialchars($vehiculo['numero_motor']); ?>">
    
                    <label for="tipo_combustible">Tipo de Combustible:</label>
                    <input type="text" id="tipo_combustible" name="tipo_combustible" value="<?php echo htmlspecialchars($vehiculo['tipo_combustible']); ?>">
    
                    <label for="propietario_nombre">Nombre del Propietario:</label>
                    <input type="text" id="propietario_nombre" name="propietario_nombre" value="<?php echo htmlspecialchars($vehiculo['propietario_nombre']); ?>">
    
                    <input type="submit" value="<?php echo ($accion == 'crear' ? 'Agregar' : 'Actualizar'); ?> Vehículo">
                </form>
                <p><a href="vehiculos.php">Volver al Listado de Vehículos</a></p>
    
            <?php else: // Listar Vehículos ?>
                <p><a href="vehiculos.php?accion=crear">Agregar Nuevo Vehículo</a></p>
                <?php
                $sql = "SELECT id_vehiculo, marca, modelo, año, placa, propietario_nombre FROM Vehiculos ORDER BY marca, modelo";
                $result = $conn->query($sql);
    
                if ($result->num_rows > 0) {
                    echo "<table>";
                    echo "<tr><th>ID</th><th>Marca</th><th>Modelo</th><th>Año</th><th>Placa</th><th>Propietario</th><th class='actions'>Acciones</th></tr>";
                    while($row = $result->fetch_assoc()) {
                        echo "<tr>";
                        echo "<td>" . htmlspecialchars($row["id_vehiculo"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["marca"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["modelo"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["año"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["placa"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["propietario_nombre"]). "</td>";
                        echo "<td class='actions'>";
                        echo "<a href='vehiculos.php?accion=editar&id=" . htmlspecialchars($row["id_vehiculo"]) . "'>Editar</a>";
                        if (check_role('administrador')) { // Solo admin puede eliminar
                            echo "<a href='vehiculos.php?accion=eliminar&id=" . htmlspecialchars($row["id_vehiculo"]) . "' onclick='return confirm(\"¿Estás seguro de eliminar este vehículo?\")'>Eliminar</a>";
                        }
                        echo "</td>";
                        echo "</tr>";
                    }
                    echo "</table>";
                } else {
                    echo "<p>No hay vehículos registrados.</p>";
                }
                $conn->close();
                ?>
            <?php endif; ?>
        </div>
    </body>
    </html>
    

8. empleados.php (CRUD de Empleados)

Similar a vehiculos.php, pero para la tabla Empleados. Se omitirá el código completo por brevedad, pero la estructura es análoga:

  • Lógica PHP para GET (listar, editar, eliminar) y POST (crear, actualizar).
  • Formulario HTML para crear/editar empleados.
  • Tabla HTML para listar empleados.
  • Control de Acceso: Las operaciones de creación, edición y eliminación de Empleados deben ser exclusivas para usuarios con rol = 'administrador' (usar check_role('administrador')).

9. mantenimientos.php (CRUD y Asignación de Mantenimientos)

Este es más complejo, ya que implica relaciones con Vehiculos y Empleados.

  • Lógica Principal (mantenimientos.php):
    PHP
    <?php
    session_start();
    include 'includes/conexion.php';
    include 'includes/funciones.php';
    
    if (!is_logged_in()) {
        $_SESSION['error'] = "Debes iniciar sesión para acceder a esta página.";
        redireccionar("login.php");
    }
    
    $accion = $_GET['accion'] ?? 'listar';
    $id = $_GET['id'] ?? null;
    
    // Obtener vehículos y empleados para los selectbox del formulario
    $vehiculos_disponibles = [];
    $empleados_disponibles = [];
    
    $res_vehiculos = $conn->query("SELECT id_vehiculo, placa, marca, modelo FROM Vehiculos ORDER BY placa");
    while ($row = $res_vehiculos->fetch_assoc()) {
        $vehiculos_disponibles[$row['id_vehiculo']] = htmlspecialchars($row['placa'] . " (" . $row['marca'] . " " . $row['modelo'] . ")");
    }
    
    $res_empleados = $conn->query("SELECT id_empleado, nombre, apellido FROM Empleados ORDER BY nombre");
    while ($row = $res_empleados->fetch_assoc()) {
        $empleados_disponibles[$row['id_empleado']] = htmlspecialchars($row['nombre'] . " " . $row['apellido']);
    }
    
    // --- Lógica CRUD para Mantenimientos ---
    if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['form_mantenimiento'])) {
        $id_mantenimiento = sanear_dato($conn, $_POST['id_mantenimiento']);
        $id_vehiculo = sanear_dato($conn, $_POST['id_vehiculo']);
        $id_empleado = sanear_dato($conn, $_POST['id_empleado']);
        $fecha_mantenimiento = sanear_dato($conn, $_POST['fecha_mantenimiento']);
        $tipo_mantenimiento = sanear_dato($conn, $_POST['tipo_mantenimiento']);
        $costo = sanear_dato($conn, $_POST['costo']);
        $observaciones = sanear_dato($conn, $_POST['observaciones']);
    
        if (empty($id_vehiculo) || empty($id_empleado) || empty($fecha_mantenimiento) || empty($tipo_mantenimiento)) {
            $_SESSION['error'] = "Vehículo, Empleado, Fecha y Tipo de Mantenimiento son obligatorios.";
            redireccionar("mantenimientos.php?accion=" . (empty($id_mantenimiento) ? 'crear' : 'editar&id=' . $id_mantenimiento));
        } else {
            if (empty($id_mantenimiento)) { // CREAR
                $stmt = $conn->prepare("INSERT INTO Mantenimientos (id_vehiculo, id_empleado, fecha_mantenimiento, tipo_mantenimiento, costo, observaciones) VALUES (?, ?, ?, ?, ?, ?)");
                $stmt->bind_param("iissss", $id_vehiculo, $id_empleado, $fecha_mantenimiento, $tipo_mantenimiento, $costo, $observaciones);
                if ($stmt->execute()) {
                    $_SESSION['mensaje'] = "Mantenimiento agregado exitosamente.";
                } else {
                    $_SESSION['error'] = "Error al agregar mantenimiento: " . $stmt->error;
                }
            } else { // ACTUALIZAR
                // Control de acceso: Solo admin o empleado responsable puede modificar?
                if (!check_role('administrador')) {
                    // Aquí se puede agregar una lógica más compleja: un empleado solo edita mantenimientos que él mismo hizo
                    // $stmt_check_empleado = $conn->prepare("SELECT id_empleado FROM Mantenimientos WHERE id_mantenimiento = ?");
                    // $stmt_check_empleado->bind_param("i", $id_mantenimiento);
                    // $stmt_check_empleado->execute();
                    // $res_check = $stmt_check_empleado->get_result()->fetch_assoc();
                    // if ($res_check['id_empleado'] != $_SESSION['id_empleado_asociado_al_usuario_logueado']) { ... }
                    $_SESSION['error'] = "No tienes permiso para modificar este mantenimiento.";
                    redireccionar("mantenimientos.php");
                }
                $stmt = $conn->prepare("UPDATE Mantenimientos SET id_vehiculo=?, id_empleado=?, fecha_mantenimiento=?, tipo_mantenimiento=?, costo=?, observaciones=? WHERE id_mantenimiento=?");
                $stmt->bind_param("iissssi", $id_vehiculo, $id_empleado, $fecha_mantenimiento, $tipo_mantenimiento, $costo, $observaciones, $id_mantenimiento);
                if ($stmt->execute()) {
                    $_SESSION['mensaje'] = "Mantenimiento actualizado exitosamente.";
                } else {
                    $_SESSION['error'] = "Error al actualizar mantenimiento: " . $stmt->error;
                }
            }
            $stmt->close();
            redireccionar("mantenimientos.php");
        }
    }
    
    // Eliminar Mantenimiento
    if ($accion == 'eliminar' && $id && check_role('administrador')) { // Solo admin puede eliminar
        $stmt = $conn->prepare("DELETE FROM Mantenimientos WHERE id_mantenimiento = ?");
        $stmt->bind_param("i", $id);
        if ($stmt->execute()) {
            $_SESSION['mensaje'] = "Mantenimiento eliminado exitosamente.";
        } else {
            $_SESSION['error'] = "Error al eliminar mantenimiento: " . $stmt->error;
        }
        $stmt->close();
        redireccionar("mantenimientos.php");
    } elseif ($accion == 'eliminar' && !check_role('administrador')) {
         $_SESSION['error'] = "No tienes permiso para eliminar mantenimientos.";
         redireccionar("mantenimientos.php");
    }
    
    // --- HTML y Vistas ---
    ?>
    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Gestión de Mantenimientos</title>
        <link rel="stylesheet" href="css/style.css">
    </head>
    <body>
        <div class="navbar">
            <a href="index.php">Inicio</a>
            <a href="dashboard.php">Dashboard</a>
            <a href="vehiculos.php">Vehículos</a>
            <a href="empleados.php">Empleados</a>
            <a href="mantenimientos.php">Mantenimientos</a>
            <div class="right">
                <a href="perfil.php">Mi Perfil (<?php echo htmlspecialchars($_SESSION['nombre_usuario']); ?>)</a>
                <a href="logout.php">Cerrar Sesión</a>
            </div>
        </div>
        <div class="container">
            <h1>Gestión de Mantenimientos</h1>
            <?php mostrar_mensaje(); ?>
            <?php mostrar_error(); ?>
    
            <?php if ($accion == 'crear' || $accion == 'editar'):
                $mantenimiento = ['id_mantenimiento' => '', 'id_vehiculo' => '', 'id_empleado' => '', 'fecha_mantenimiento' => '', 'tipo_mantenimiento' => '', 'costo' => '', 'observaciones' => ''];
                if ($accion == 'editar' && $id) {
                    $stmt = $conn->prepare("SELECT * FROM Mantenimientos WHERE id_mantenimiento = ?");
                    $stmt->bind_param("i", $id);
                    $stmt->execute();
                    $result = $stmt->get_result();
                    if ($result->num_rows == 1) {
                        $mantenimiento = $result->fetch_assoc();
                    } else {
                        $_SESSION['error'] = "Mantenimiento no encontrado.";
                        redireccionar("mantenimientos.php");
                    }
                    $stmt->close();
                }
            ?>
                <h2><?php echo ($accion == 'crear' ? 'Registrar Nuevo' : 'Editar'); ?> Mantenimiento</h2>
                <form action="mantenimientos.php" method="POST">
                    <input type="hidden" name="form_mantenimiento" value="1">
                    <input type="hidden" name="id_mantenimiento" value="<?php echo htmlspecialchars($mantenimiento['id_mantenimiento']); ?>">
    
                    <label for="id_vehiculo">Vehículo:</label>
                    <select id="id_vehiculo" name="id_vehiculo" required>
                        <option value="">Seleccione un vehículo</option>
                        <?php foreach ($vehiculos_disponibles as $id_v => $desc_v): ?>
                            <option value="<?php echo $id_v; ?>" <?php echo ($mantenimiento['id_vehiculo'] == $id_v ? 'selected' : ''); ?>>
                                <?php echo $desc_v; ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
    
                    <label for="id_empleado">Empleado Responsable:</label>
                    <select id="id_empleado" name="id_empleado" required>
                        <option value="">Seleccione un empleado</option>
                        <?php foreach ($empleados_disponibles as $id_e => $desc_e): ?>
                            <option value="<?php echo $id_e; ?>" <?php echo ($mantenimiento['id_empleado'] == $id_e ? 'selected' : ''); ?>>
                                <?php echo $desc_e; ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
    
                    <label for="fecha_mantenimiento">Fecha de Mantenimiento:</label>
                    <input type="date" id="fecha_mantenimiento" name="fecha_mantenimiento" value="<?php echo htmlspecialchars($mantenimiento['fecha_mantenimiento']); ?>" required>
    
                    <label for="tipo_mantenimiento">Tipo de Mantenimiento:</label>
                    <input type="text" id="tipo_mantenimiento" name="tipo_mantenimiento" value="<?php echo htmlspecialchars($mantenimiento['tipo_mantenimiento']); ?>" required>
    
                    <label for="costo">Costo:</label>
                    <input type="number" step="0.01" id="costo" name="costo" value="<?php echo htmlspecialchars($mantenimiento['costo']); ?>">
    
                    <label for="observaciones">Observaciones:</label>
                    <textarea id="observaciones" name="observaciones" rows="4"><?php echo htmlspecialchars($mantenimiento['observaciones']); ?></textarea>
    
                    <input type="submit" value="<?php echo ($accion == 'crear' ? 'Registrar' : 'Actualizar'); ?> Mantenimiento">
                </form>
                <p><a href="mantenimientos.php">Volver al Listado de Mantenimientos</a></p>
    
            <?php else: // Listar Mantenimientos ?>
                <p><a href="mantenimientos.php?accion=crear">Registrar Nuevo Mantenimiento</a></p>
                <?php
                // Consulta avanzada con JOIN para mostrar información de Vehículos y Empleados
                $sql = "SELECT
                            m.id_mantenimiento,
                            m.fecha_mantenimiento,
                            m.tipo_mantenimiento,
                            m.costo,
                            v.placa AS vehiculo_placa,
                            v.marca AS vehiculo_marca,
                            e.nombre AS empleado_nombre,
                            e.apellido AS empleado_apellido
                        FROM
                            Mantenimientos m
                        JOIN
                            Vehiculos v ON m.id_vehiculo = v.id_vehiculo
                        JOIN
                            Empleados e ON m.id_empleado = e.id_empleado
                        ORDER BY m.fecha_mantenimiento DESC";
                $result = $conn->query($sql);
    
                if ($result->num_rows > 0) {
                    echo "<table>";
                    echo "<tr><th>ID</th><th>Vehículo (Placa)</th><th>Vehículo (Marca)</th><th>Empleado</th><th>Fecha</th><th>Tipo</th><th>Costo</th><th class='actions'>Acciones</th></tr>";
                    while($row = $result->fetch_assoc()) {
                        echo "<tr>";
                        echo "<td>" . htmlspecialchars($row["id_mantenimiento"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["vehiculo_placa"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["vehiculo_marca"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["empleado_nombre"] . " " . $row["empleado_apellido"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["fecha_mantenimiento"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["tipo_mantenimiento"]). "</td>";
                        echo "<td>" . htmlspecialchars($row["costo"]). "</td>";
                        echo "<td class='actions'>";
                        echo "<a href='mantenimientos.php?accion=editar&id=" . htmlspecialchars($row["id_mantenimiento"]) . "'>Editar</a>";
                        if (check_role('administrador')) {
                            echo "<a href='mantenimientos.php?accion=eliminar&id=" . htmlspecialchars($row["id_mantenimiento"]) . "' onclick='return confirm(\"¿Estás seguro de eliminar este mantenimiento?\")'>Eliminar</a>";
                        }
                        echo "</td>";
                        echo "</tr>";
                    }
                    echo "</table>";
                } else {
                    echo "<p>No hay mantenimientos registrados.</p>";
                }
                $conn->close();
                ?>
            <?php endif; ?>
        </div>
    </body>
    </html>
    

10. Asignación de Registros (Ejemplo en mantenimientos.php)

La asignación de registros se realiza automáticamente al insertar o actualizar un Mantenimiento porque se seleccionan id_vehiculo y id_empleado desde las tablas respectivas. Estos campos son las claves foráneas que establecen las relaciones.

  • Reglas de Negocio para Asignaciones:
    • Unicidad (Implícita): Cada registro de mantenimiento es único para una fecha, vehículo y tipo de mantenimiento.
    • Existencia: No se puede asignar un mantenimiento a un Vehiculo o Empleado que no exista en sus respectivas tablas (FOREIGN KEY lo impone).
    • Disponibilidad: Podrías implementar una lógica más compleja (en PHP y SQL) para verificar si un empleado está disponible en una fecha específica para realizar un mantenimiento, o si un vehículo ya tiene un mantenimiento programado para ese día. Esto requeriría añadir columnas como hora_inicio, hora_fin en Mantenimientos y lógica de validación PHP.

11. Consultas Avanzadas (Ejemplo en mantenimientos.php)

La consulta para listar mantenimientos (m.id_mantenimiento, m.fecha_mantenimiento, ... JOIN Vehiculos v ON m.id_vehiculo = v.id_vehiculo JOIN Empleados e ON m.id_empleado = e.id_empleado) es un ejemplo de consulta avanzada que involucra múltiples tablas (Mantenimientos, Vehiculos, Empleados) y utiliza JOIN para combinar datos relacionados, mostrando información consolidada en una única tabla HTML.

12. Gestión de Seguridad de Usuarios por Variables de Sesión ($_SESSION)

  • Inicio de Sesión (login.php): Al autenticar, se guarda id_usuario, nombre_usuario y rol en $_SESSION.
  • Restricción de Acceso (dashboard.php, vehiculos.php, etc.): Se usa is_logged_in() al inicio de cada página para redirigir si no hay sesión activa.
  • Control de Permisos (check_role()): En vehiculos.php y mantenimientos.php, las acciones de eliminar y ciertas ediciones están protegidas con if (check_role('administrador')). Esto evita que un usuario con rol 'usuario' pueda realizar operaciones críticas.
  • Modificación de Perfil (perfil.php): Permite al usuario editar solo sus propios datos, usando $_SESSION['id_usuario'] en la cláusula WHERE de las consultas UPDATE. Esto es un control de acceso vital a nivel de fila.

Este tutorial proporciona un esqueleto funcional para tu aplicación. Para un proyecto real, considera:

  • Validación de Formulario más robusta: Validaciones JavaScript en el frontend y más estrictas en el backend.
  • Manejo de Errores: Bloques try-catch para manejo de excepciones en la base de datos.
  • Seguridad: Uso de sentencias preparadas para todas las consultas con variables de usuario ($stmt->bind_param()) es CRÍTICO para prevenir inyecciones SQL. Este tutorial ya las usa, pero es importante entender su relevancia.
  • Paginación: Para tablas con muchos registros.
  • Búsqueda y Filtrado: Funcionalidad para encontrar registros específicos.
  • Modularización: Separar el código PHP en funciones y clases para mejor mantenimiento.

Este es un punto de partida sólido para tu aplicación web. ¡Continúa experimentando y construyendo!


Referencias

American Psychological Association. (2020). Publication Manual of the American Psychological Association (7th ed.). American Psychological Association.


Comentarios