# 🚩 Ejercicio 5 · La base que no arranca

**Clase 5 — Base de datos, migraciones y seeders**
· Dificultad: 🟠 medio-alto · Tiempo estimado: 60–90 min

---

## La situación

Llegó el momento: el Liquidador deja de tener los datos "a mano" y pasa a
guardarlos en una base de datos de verdad. Pero la primera corrida termina así:

```
  ✘  EmpleadoSeeder    SQLSTATE[HY000]: table empleados has no column named legajo
```

Las migraciones están a medio hacer, corren en el orden equivocado y el seeder
de departamentos está vacío.

> **Esta vez la base es real.** Usamos **SQLite**, que viene incluida con PHP:
> no hay que instalar ni configurar nada, pero es una base de datos de verdad,
> con SQL, tipos y restricciones de integridad. Todo lo que escribas acá es
> igual en MySQL — de eso se trata el Query Builder.

## Lo que hace especial a este ejercicio

Cada corrida te muestra **el SQL que genera tu migración**:

```
│ CREATE TABLE empleados (
│   id INTEGER PRIMARY KEY AUTOINCREMENT,
│   nombre VARCHAR(255) NOT NULL,
│   legajo VARCHAR(255) NOT NULL UNIQUE,
│   sueldo_basico DECIMAL(12,2) NOT NULL DEFAULT 0,
│   departamento_id INTEGER NOT NULL,
│   FOREIGN KEY (departamento_id) REFERENCES departamentos(id)
│ )
```

Eso es exactamente lo que Laravel escribe por vos cuando corrés `artisan migrate`.
Verlo al lado de tu código PHP es la mejor forma de entender qué hace realmente
una migración.

## Tus cinco tareas

| # | Dónde | Qué |
|---|---|---|
| 1 | `migraciones/` | **Arreglar el orden** en que corren |
| 2 | la migración de empleados | Completar **columnas y clave foránea** |
| 3 | — | (sale solo cuando el paso 2 está bien) |
| 4 | `seeders/DepartamentoSeeder.php` | **Sembrar** los tres departamentos |
| 5 | `consultas.php` | Escribir la consulta con **Query Builder** |

### El orden de las migraciones

El migrador corre los archivos de `migraciones/` **ordenados por nombre**. Por eso
las migraciones de Laravel empiezan con una marca de tiempo: el nombre define
cuándo se ejecuta cada una.

Hoy la de empleados corre primero… y necesita que `departamentos` ya exista.
**Renombrá el archivo** para que corra después.

> 💡 El nombre exacto que elijas no importa: lo que se verifica es el **orden**.

### La tabla empleados

```
  id                 clave primaria
  nombre             texto
  legajo             texto, único
  sueldo_basico      decimal(12,2), por defecto 0
  departamento_id    apunta a departamentos(id), con su restricción
  created_at / updated_at
```

Los métodos disponibles están documentados arriba de `src/nucleo/Blueprint.php`.
Usá la migración de departamentos como modelo.

### Lo que pide RRHH (paso 5)

> *"Dame los empleados que cobran **más de $1.000.000** de básico, del más alto
> al más bajo, y solo el nombre y el sueldo."*

Leelo con atención. Hay una empleada que cobra **exactamente** un millón, y eso
cambia el resultado según cómo escribas la condición.

## Cómo correrlo

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

La base se crea desde cero en cada corrida, así que podés experimentar sin miedo:
es el equivalente de `php artisan migrate:fresh --seed`.

Con los cinco pasos en verde aparece tu bandera `ipap{c05-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 | Por qué las migraciones llevan **marca de tiempo** en el nombre |
| 2 | Definir un **esquema en código**: columnas, tipos, `unique`, `default` |
| 3 | **Integridad referencial**: la base rechaza datos que no cierran |
| 4 | **Seeders**: los datos iniciales, versionados junto al código |
| 5 | **Query Builder**: consultar encadenando métodos |

## Si te trabás

1. **El orden.** El error te lo dice con todas las letras: no puede crear una
   tabla con una clave foránea hacia otra que todavía no existe.
2. **Las columnas.** Mirá el SQL generado en la sección 2 de la salida: ahí ves
   exactamente qué columnas tiene hoy tu tabla y cuáles faltan.
3. **El paso 3 no se resuelve solo.** Depende de que hayas puesto el
   `->constrained('departamentos')`. Sin eso, la base acepta un empleado de un
   departamento inexistente.
4. **El seeder.** El orden de los tres departamentos importa: los ids se asignan
   en el orden en que los insertás, y el seeder de empleados cuenta con eso.
5. **La consulta.** Los métodos disponibles están arriba de `src/nucleo/BD.php`.
   Se encadenan igual que en Laravel; el `->get()` lo hace el programa por vos.

---

## Un detalle que vale la pena saber

En el paso 1 el error aparece **al crear la tabla**. Eso es lo que hace MySQL, que
es el motor del curso.

**SQLite es más permisivo:** te deja crear una tabla con una clave foránea hacia
otra que no existe, y recién protesta cuando intentás insertar. El `Schema` de
este ejercicio replica a propósito el comportamiento de MySQL, para que el error
aparezca donde tiene sentido.

Es un buen ejemplo de algo que vas a encontrar seguido: el SQL es un estándar,
pero cada motor tiene su carácter.

## Lo interesante

Fijate que **nunca escribiste SQL**. Describiste la tabla en PHP y el framework
generó el `CREATE TABLE`; armaste la consulta encadenando métodos y él generó el
`SELECT`. Y sin embargo tenés una base de datos real, con tipos y restricciones.

Eso tiene dos consecuencias grandes:

- **El esquema se versiona en Git**, como el código. Nadie más "arregla algo a
  mano en producción y se olvida de avisar".
- **Los valores viajan aparte del SQL** (fijate en `->execute($this->valores)`).
  Por eso el Query Builder te protege de inyección SQL sin que hagas nada.

En la Clase 6, Eloquent va a poner una capa más arriba de esto: en vez de
`BD::tabla('empleados')->where(...)` vas a escribir `Empleado::where(...)`, y cada
fila va a ser un objeto.
