# 🚩 Ejercicio 3 · La ruta que no lleva a ninguna parte

**Clase 3 — MVC, ruteo y el ciclo de una petición**
· Dificultad: 🟡 intermedio · Tiempo estimado: 60–75 min

---

## La situación

El Liquidador tiene un módulo web con tres rutas. Dos problemas:

```
GET  /empleados/2                el detalle de Ana        404  ← debería andar
POST /admin/liquidar             liquidar SIN clave       200  ← NO debería andar
```

Una ruta que existe en el papel **no responde**, y una acción sensible **la puede
ejecutar cualquiera**. Tu trabajo es arreglar las dos cosas.

## Lo que hace especial a este ejercicio

Cada vez que lo corras vas a ver **el recorrido completo de la petición por dentro**:

```
  ├─ 1 · Request               GET /empleados/2
  ├─ 2 · index.php             única puerta de entrada
  ├─ 3 · Middleware global     RegistrarPeticion
  ├─ 4 · Router                ninguna ruta coincide → 404
  └─ 8 · Response              404 — vuelve al navegador
```

Ese es el diagrama de la clase, ejecutándose. **Y te dice exactamente dónde se
corta**: acá la petición murió en el paso 4 y nunca llegó al controller.

No es Laravel: es un mini-framework de ~90 líneas que hace lo mismo en chiquito.
Está en `src/nucleo/Router.php` y **vale la pena leerlo entero**: si entendés ese
archivo, entendiste la clase. Laravel hace exactamente esto, con más features.

## Tus cuatro tareas

| # | Dónde | Qué |
|---|---|---|
| 1 | `src/rutas.php` | Registrar la ruta `GET /empleados/{id}` |
| 2 | `src/app/Controllers/EmpleadoController.php` | Escribir el método `show()` |
| 3 | `src/app/Middleware/VerificarClave.php` | Crear el middleware (el archivo no existe) |
| 4 | `src/rutas.php` | Aplicar ese middleware a `POST /admin/liquidar` |

### La regla del middleware

> `/admin/liquidar` solo se puede ejecutar con la clave correcta:
> **sin `?clave=ipap` tiene que devolver 403 y no llegar nunca al controller.**

## Cómo correrlo

```bash
docker compose run --rm ejercicio
```

Te muestra las seis peticiones de prueba con su código de estado, el recorrido
interno, y en qué paso vas:

```
  ✔  Paso 1 · Está registrada la ruta GET /empleados/{id}
  ✘  Paso 2 · EmpleadoController@show devuelve el empleado correcto
```

Cuando los cinco estén en verde aparece tu bandera `ipap{c03-xxxxxxxxxx}`, que
—como siempre— **no está escrita en ningún archivo**: se calcula a partir de las
rutas que registraste y de los códigos de estado con los que contesta tu app.

> **Sin Docker:** `cd src && php index.php`

## Lo que vas a practicar

| Paso | Concepto de la clase |
|------|----------------------|
| 1 | **Routing**: el mapa URL → controlador, con parámetros `{id}` |
| 2 | **Controller**: la "C" de MVC. Coordina, no calcula |
| 3 | **Middleware**: un filtro que deja pasar o corta |
| 4 | Middleware **de ruta** (no global) — y por qué importa la diferencia |
| 5 | **Códigos de estado**: 200, 403 y 404 según lo que pasó |

## Si te trabás

1. **La ruta.** Mirá cómo está registrada `GET /empleados` justo arriba. El `{id}`
   va entre llaves; el nombre que le pongas es el que después leés con
   `$peticion->parametro('...')`.
2. **El 404 del empleado inexistente.** Ojo con esto: que `/empleados/99` devuelva
   404 *porque no registraste la ruta* no cuenta. Tiene que llegar al controller y
   que sea **el controller** el que decida que no existe. Mirá el recorrido: si no
   aparece el paso 6, no llegó.
3. **El middleware.** Copiá la estructura de `RegistrarPeticion`, que ya está hecho.
   La única diferencia es que el tuyo, a veces, **no** llama a `$siguiente()`.
4. **Aplicarlo.** Se encadena a la ruta con `->middleware(Clase::class)`, igual que
   en Laravel.
5. **No lo registres como global.** Si lo hacés, vas a proteger *todas* las rutas y
   el listado de empleados va a empezar a devolver 403. Probalo si querés: se ve
   clarísimo por qué existe la diferencia entre middleware global y de ruta.

## Los archivos

| Archivo | |
|---|---|
| `src/nucleo/Router.php` | ⭐ El ciclo de vida completo en 90 líneas. **Leelo.** |
| `src/nucleo/Peticion.php` · `Respuesta.php` · `Middleware.php` | Las piezas del framework |
| `src/rutas.php` | 🚩 El mapa. Trabajás acá (pasos 1 y 4) |
| `src/app/Controllers/EmpleadoController.php` | 🚩 Trabajás acá (paso 2) |
| `src/app/Middleware/VerificarClave.php` | 🚩 **No existe. Lo creás vos** (paso 3) |
| `src/app/Middleware/RegistrarPeticion.php` | Middleware global de ejemplo |
| `src/index.php` | La puerta de entrada y el simulador (no se toca) |

---

## Lo interesante

Prestá atención a **dónde** quedó el control de acceso: en el middleware, no en el
`AdminController`. El controller no sabe que existe una clave; solo hace su trabajo.

Eso permite proteger diez rutas más sin tocar ni un controlador, y es la razón por
la que en la Clase 9 vamos a poder poner autenticación real sobre la API del
Liquidador sin reescribir la lógica de negocio.

Y fijate en el orden del recorrido: el middleware de ruta corre **después** del
router. Tiene que ser así — hasta que el router no resolvió qué ruta es, la
aplicación no sabe qué middleware le corresponden.
