Curso de Laravel

Seeders en Laravel: poblar la base de datos

Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de Laravel, hoy veremos los seeders: la forma de llenar la base de datos con información sin escribir un solo INSERT a mano.

¡Empecemos!

Para qué sirven

Si las migraciones versionan la estructura de la base, los seeders versionan los datos que siempre deben estar ahí.

Dos usos que conviene distinguir desde el principio:

Datos de prueba. Necesitas cincuenta pacientes para ver cómo se comporta la paginación. No vas a crearlos a mano.

Datos reales del sistema. Hay información que la aplicación necesita para funcionar: las especialidades médicas, los roles de usuario, los estados de un pedido, los departamentos del país. Sin ellos el sistema no arranca.

Ese segundo caso es el más importante y el que más se pasa por alto. Un compañero que clona el proyecto debe poder tenerlo funcionando con dos comandos.

Crear un seeder

php artisan make:seeder EspecialidadSeeder

Se crea en database/seeders/:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\Especialidad;

class EspecialidadSeeder extends Seeder
{
    public function run(): void
    {
        $especialidades = [
            ['nombre' => 'Cardiología', 'descripcion' => 'Corazón y sistema circulatorio'],
            ['nombre' => 'Pediatría', 'descripcion' => 'Atención médica a niños'],
            ['nombre' => 'Traumatología', 'descripcion' => 'Huesos y articulaciones'],
            ['nombre' => 'Dermatología', 'descripcion' => 'Enfermedades de la piel'],
        ];

        foreach ($especialidades as $especialidad) {
            Especialidad::create($especialidad);
        }
    }
}

Y se ejecuta:

php artisan db:seed --class=EspecialidadSeeder

El DatabaseSeeder

Es el seeder principal, el que se ejecuta por defecto. Desde ahí se llaman los demás:

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([
            EspecialidadSeeder::class,
            DoctorSeeder::class,
            PacienteSeeder::class,
            CitaSeeder::class,
        ]);
    }
}
php artisan db:seed

El orden importa. Si DoctorSeeder crea doctores con su especialidad_id, las especialidades tienen que existir antes. Es la misma lógica de las claves foráneas que vimos en MySQL.

El comando que más vas a usar

php artisan migrate:fresh --seed

Borra todas las tablas, vuelve a crearlas y ejecuta los seeders. En una máquina de desarrollo es lo que usas cada vez que quieres empezar limpio.

Y el aviso de siempre: en producción borra todos los datos. Antes de escribirlo, mira en qué entorno estás.

Insertar muchos registros

Crear los registros uno por uno con create() es cómodo pero lento: cada llamada es una consulta a la base.

Para volúmenes grandes, insert() hace una sola consulta:

public function run(): void
{
    $registros = [];

    for ($i = 1; $i <= 1000; $i++) {
        $registros[] = [
            'nombre' => "Paciente $i",
            'cedula' => str_pad((string) $i, 7, '0', STR_PAD_LEFT),
            'created_at' => now(),
            'updated_at' => now(),
        ];
    }

    Paciente::insert($registros);
}

Dos advertencias sobre insert():

  • No dispara los eventos del modelo ni aplica los casts. Es SQL casi directo.
  • Hay que poner created_at y updated_at a mano, porque Eloquent no interviene.

Y si son decenas de miles, insértalos por lotes para no agotar la memoria:

foreach (array_chunk($registros, 500) as $lote) {
    Paciente::insert($lote);
}

Seeders para datos reales del sistema

Aquí está el uso más valioso, y conviene escribirlos de forma que se puedan ejecutar más de una vez sin duplicar:

class EstadoCitaSeeder extends Seeder
{
    public function run(): void
    {
        $estados = ['programada', 'atendida', 'cancelada', 'reprogramada'];

        foreach ($estados as $estado) {
            EstadoCita::firstOrCreate(['nombre' => $estado]);
        }
    }
}

Ese firstOrCreate() es la clave: si el estado ya existe, no hace nada. Así puedes ejecutar el seeder en producción cuando añadas un estado nuevo, sin miedo a duplicar los existentes.

Es la forma correcta de introducir datos de catálogo en un sistema ya desplegado.

Seeders con relaciones

class CitaSeeder extends Seeder
{
    public function run(): void
    {
        $pacientes = Paciente::all();
        $doctores = Doctor::all();

        foreach ($pacientes as $paciente) {
            Cita::create([
                'paciente_id' => $paciente->id,
                'doctor_id' => $doctores->random()->id,
                'fecha' => now()->addDays(rand(1, 30)),
                'hora' => '09:00:00',
                'costo' => 250,
                'estado' => 'programada',
            ]);
        }
    }
}

Fíjate en $doctores->random(): las colecciones de Eloquent traen métodos muy cómodos para este tipo de cosas.

Distinguir por entorno

Un seeder puede comportarse distinto según dónde se ejecute:

public function run(): void
{
    // Datos que el sistema necesita siempre
    $this->call([
        EspecialidadSeeder::class,
        RolSeeder::class,
    ]);

    // Datos de prueba, solo fuera de producción
    if (! app()->environment('production')) {
        $this->call([
            PacienteDePruebaSeeder::class,
            CitaDePruebaSeeder::class,
        ]);
    }
}

Esa separación evita el accidente de llenar producción con «Paciente 1», «Paciente 2».

Usar factories desde un seeder

Escribir a mano cincuenta pacientes con datos creíbles es tedioso. Para eso están los factories, que veremos en la siguiente lección, pero adelanto cómo se combinan:

public function run(): void
{
    Paciente::factory()->count(50)->create();
}

Una línea, cincuenta pacientes con nombres, cédulas y teléfonos verosímiles.

La división de trabajo: el factory define cómo se ve un registro de prueba; el seeder decide cuántos y en qué orden.

Un seeder con salida en pantalla

Para seeders que tardan, conviene informar:

public function run(): void
{
    $this->command->info('Creando pacientes...');

    $barra = $this->command->getOutput()->createProgressBar(1000);

    for ($i = 0; $i < 1000; $i++) {
        Paciente::factory()->create();
        $barra->advance();
    }

    $barra->finish();
    $this->command->info("\nListo.");
}

Errores comunes

  • Orden incorrecto en el DatabaseSeeder, fallando por clave foránea.
  • No usar firstOrCreate() en datos de catálogo, y duplicarlos al reejecutar.
  • Insertar miles de registros uno por uno en lugar de por lotes.
  • Ejecutar migrate:fresh --seed en producción.
  • Mezclar datos de prueba con datos del sistema en el mismo seeder.
  • Olvidar created_at al usar insert().

Para cerrar

Los seeders convierten «clona el proyecto y pídeme el volcado de la base» en «clona el proyecto y ejecuta dos comandos». Eso es lo que hacen valiosos.

La distinción que quiero que te lleves: datos de catálogo con firstOrCreate() para poder reejecutar, y datos de prueba separados y solo fuera de producción.

En la siguiente lección veremos los factories, que generan datos falsos verosímiles.

Saludos y éxitos.

Norvic Software

Desarrollamos el software que tu empresa necesita

Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp