# 🚩 Ejercicio 6 · Los modelos que no se hablan

**Clase 6 — Eloquent ORM y relaciones**
· Dificultad: 🟠 medio-alto · Tiempo estimado: 60–90 min

---

## La situación

La base del Liquidador ya tiene empleados, departamentos, proyectos y hasta la
tabla pivote que los conecta. Los modelos existen. Pero no se hablan entre ellos:

```
  ✘  belongsTo      $ana->departamento      (no declarada)
  ✘  hasMany        $sistemas->empleados    (no declarada)
  ✘  belongsToMany  $ana->proyectos         (no declarada)
```

Y `Empleado::crear()` ni siquiera puede dar de alta a nadie.

## Lo que hace especial a este ejercicio

Cada corrida **cuenta las consultas que se mandan a la base**:

```
  Recorriendo sin precargar              9 consultas   ← 1 lista + 1 por empleado
  Tu informe                             2 consultas   ← precarga en una sola
```

Ese es el problema **N+1**, en números. Con 8 empleados la diferencia parece
poca. Con 5.000 son 5.001 consultas contra 2: la misma página, o instantánea o
caída.

Es la única forma honesta de verificar el *eager loading*, porque el resultado
con y sin él es **idéntico**. Lo que cambia es el costo.

## Tus cinco tareas

| # | Dónde | Qué |
|---|---|---|
| 1 | `app/Modelos/Empleado.php` | Completar el **`$fillable`** |
| 2 | `app/Modelos/Empleado.php` | La relación **`belongsTo`** con departamento |
| 3 | `app/Modelos/Departamento.php` | La relación **`hasMany`** con empleados |
| 4 | `app/Modelos/Empleado.php` | La relación **`belongsToMany`** con proyectos |
| 5 | `informe.php` | Evitar el **N+1** con eager loading |

Los métodos disponibles están documentados arriba de `src/nucleo/Modelo.php`.

## Cómo correrlo

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

Con los cinco pasos en verde aparece tu bandera `ipap{c06-xxxxxxxxxx}`, que
—como siempre— **no está escrita en ningún archivo**.

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

## Lo que vas a practicar

| Paso | Concepto de la clase |
|------|----------------------|
| 1 | **Asignación masiva** y por qué existe la lista blanca |
| 2 | **`belongsTo`**: el lado que tiene la clave foránea |
| 3 | **`hasMany`**: el otro lado de la misma relación |
| 4 | **`belongsToMany`** y la **tabla pivote** |
| 5 | **Eager loading** y el problema **N+1** |

## Si te trabás

1. **El `$fillable`.** El error te lo dice explícitamente. Poné exactamente los
   cuatro campos que pide el comentario — **ni uno más**. Meter `id` ahí es un
   agujero: alguien podría mandar el id de otro registro y pisarlo. El
   verificador lo comprueba mandando un `id` a propósito.
2. **La dirección de la relación.** Regla que no falla: **el lado que tiene la
   columna `_id` lleva el `perteneceA`**; el otro lado lleva el `tieneMuchos`.
   Mirá la tabla `empleados`: ahí está `departamento_id`.
3. **La pivote.** `perteneceAMuchos` lleva cuatro datos: la clase relacionada,
   el nombre de la tabla pivote, **la columna que apunta a este modelo** y la
   que apunta al otro. El orden de esos dos últimos importa: si los invertís,
   la consulta sale al revés.
4. **El eager loading.** No cambia lo que devolvés, cambia *cuándo* se traen los
   datos. Mirá el contador después de cada intento.

---

## Lo interesante

Fijate en algo que pasó sin que lo notaras: **`$ana->departamento` no es una
columna**. No existe en la tabla. Sin embargo se escribe igual que `$ana->nombre`.

Eso es lo que hace un ORM: te deja pensar en objetos que se relacionan entre sí,
en lugar de en tablas y JOINs. El precio es que resulta **muy fácil pedir datos
sin darte cuenta de cuántas consultas estás disparando** — y de ahí sale el N+1,
que es el error de performance más común con cualquier ORM, en cualquier lenguaje.

Lo peligroso es que **no se ve**: la página anda, los datos están bien, no hay
ningún error. Solo es lenta, y cada vez más a medida que crecen los datos.

Por eso el contador de este ejercicio es la herramienta más útil que te llevás:
en Laravel lo tenés con **Laravel Debugbar** o **Telescope**, y conviene mirarlo
cada tanto aunque todo parezca funcionar.
