MVC Master

Módulo 4: CRUD Estudiantes (UI Premium Glass)

Ahora crearemos nuestro primer CRUD Superior. Inserción Segura y Vista en lista combinada con Animaciones CSS, Card Layouts Full Width y Botones de Borrado con SweetAlertJS Frontend real para emular el resultado exacto de la "Demo App" Viva.

4.1: Modelo Estudiante (DB Backend)

app/Models/EstudianteModelo.php
<?php
namespace App\Models;

class EstudianteModelo extends \App\Core\ModeloBase {
    protected $tabla = 'estudiantes';

    // (C) El Create con Anti-Inyecciones Parametrizadas
    public function insertar($mat, $nom, $cor) {
        $sql = "INSERT INTO {$this->tabla} (matricula, nombre, correo) VALUES (:m, :n, :c)";
        return $this->db->prepare($sql)->execute(['m'=>$mat, 'n'=>$nom, 'c'=>$cor]);
    }

    // (U) Update masivo segun ID
    public function actualizar($id, $mat, $nom, $cor) {
        $sql = "UPDATE {$this->tabla} SET matricula=:m, nombre=:n, correo=:c WHERE id=:id";
        return $this->db->prepare($sql)->execute(['id'=>$id, 'm'=>$mat, 'n'=>$nom, 'c'=>$cor]);
    }
}

4.2: Controlador Transaccional Estudiante

app/Controllers/EstudianteController.php
<?php
namespace App\Controllers;
use App\Models\EstudianteModelo;

class EstudianteController {
    public function index() {
        $modelo = new EstudianteModelo(); 
        $view = 'estudiantes/index'; 
        // Pasamos el título y los datos al layout
        $titulo = 'Gestión de Estudiantes';
        $estudiantes = $modelo->all();
        require_once 'app/Views/layout/main.php';
    }

    public function crearVista() { 
        $view = 'estudiantes/crear'; 
        require_once 'app/Views/layout/main.php'; 
    }

    public function guardar() {
        $mod = new EstudianteModelo();
        $mod->insertar($_POST['matricula'], $_POST['nombre'], $_POST['correo']);
        header("Location: /estudiantes?msg=Guardado"); exit;
    }

    public function borrar() {
        $mod = new EstudianteModelo(); 
        $mod->eliminar($_GET['id']);
        header("Location: /estudiantes?msg=Borrado Exitoso"); exit;
    }
}

4.3: Interfaz Index (Full-Width de lujo)

Este código es idéntico al de tu entorno Demo. Aplica esquinas redondeadas 15px, badges traslúcidos con `bg-opacity-10`, y efectos Drop Shadow.

app/Views/estudiantes/index.php
<div class="card shadow-sm border-0 bg-white" style="border-radius: 15px; animation: fadeIn 0.4s ease-out;">
    <div class="d-flex justify-content-between align-items-center p-4 pb-0 mb-3">
        <h1 class="display-6 fw-bold text-dark m-0"><i class="bi bi-people-fill text-primary me-2"></i>Gestión Alumnos</h1>
        <a href="/estudiantes/crear" class="btn btn-primary shadow-sm px-4 py-3 fw-bold" style="border-radius: 12px;">Pre-Matricular</a>
    </div>

    <div class="table-responsive p-4">
        <table class="table table-hover align-middle mb-0 bg-white">
            <thead class="table-light text-muted" style="letter-spacing: 1px;">
                <tr><th>ID</th><th>MATRÍCULA</th><th>CANDIDATO</th><th>ACCIONES</th></tr>
            </thead>
            <tbody>
                <?php foreach($estudiantes as $est): ?>
                <tr>
                    <td><span class="badge bg-light text-dark border">#<?= $est->id ?></span></td>
                    <td><span class="badge bg-primary bg-opacity-10 text-primary px-3 py-2"><?= $est->matricula ?></span></td>
                    <td class="fw-bold fs-6"><?= htmlspecialchars($est->nombre) ?></td>
                    <td>
                        <a href="/estudiantes/borrar?id=<?= $est->id ?>" class="btn btn-sm btn-danger rounded-pill px-3 ms-1" onclick="lanzarAlerta(event, this.href)">Borrar</a>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</div>

<script>
function lanzarAlerta(event, urldestino) {
    event.preventDefault(); // Detiene el salto HTML WAMP
    Swal.fire({
        title: "¿Estás super seguro?", icon: "warning", showCancelButton: true,
        confirmButtonColor: "#ef4444", confirmButtonText: "Sí, aniquilar"
    }).then((r) => { if (r.isConfirmed) { window.location.href = urldestino; } });
}
</script>