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>