Seeders en Laravel - La Guía Completa

 

Seeders en Laravel - La Guía Completa

Introducción

Los Seeders son una herramienta fundamental en Laravel que te permite poblar tu base de datos con datos iniciales o de prueba de manera automática y organizada. Son especialmente útiles cuando necesitas tener datos consistentes cada vez que reinicias tu base de datos durante el desarrollo.

En este tutorial, aprenderás todo sobre los seeders, desde su creación hasta su uso avanzado, con ejemplos prácticos y explicaciones claras.

Índice

  1. ¿Qué son los Seeders?

  2. Estructura de un Seeder

  3. Creación de Seeders

  4. DatabaseSeeder: El Orquestador

  5. Ejecución de Seeders

  6. Seeders con Faker

  7. Seeders con Factories

  8. Orden de Ejecución

  9. Seeders en Producción

  10. Ejemplos Prácticos

  11. Buenas Prácticas


1. ¿Qué son los Seeders?

Los seeders son clases PHP que te permiten insertar datos en tu base de datos de manera controlada. Son ideales para:

  • Datos iniciales: Usuarios, roles, configuraciones básicas

  • Datos de prueba: Poblar tablas con datos realistas

  • Desarrollo: Mantener datos consistentes en el equipo

  • Testing: Crear escenarios de prueba repetibles

¿Por qué usar Seeders?

bash
# Sin Seeders: Pierdes datos cada vez que reinicias
php artisan migrate:fresh
# ¡Todos los datos se pierden! 😢

# Con Seeders: Los datos se restauran automáticamente
php artisan migrate:fresh --seed
# ¡Los datos vuelven a aparecer! 🎉

2. Estructura de un Seeder

Ubicación

Los seeders se encuentran en la carpeta database/seeders/:

text
database/
├── seeders/
│   ├── DatabaseSeeder.php          # Seeder principal (orquestador)
│   ├── UserSeeder.php              # Seeder para usuarios
│   ├── PastelSeeder.php            # Seeder para pasteles
│   ├── PostSeeder.php              # Seeder para posts
│   └── CategorySeeder.php          # Seeder para categorías
└── ...

Estructura básica

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // Aquí va el código para insertar datos
        DB::table('users')->insert([
            'name' => 'Victor Arana',
            'email' => 'victor@codersfree.com',
            'password' => Hash::make('12345678'),
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }
}

3. Creación de Seeders

Comando para crear un Seeder

bash
# Crear un seeder básico
php artisan make:seeder UserSeeder

# Crear un seeder con nombre específico
php artisan make:seeder PastelSeeder

# Crear un seeder con modelo (Laravel 8+)
php artisan make:seeder UserSeeder --model=User

Seeder para Usuarios

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // Método 1: Usando DB
        DB::table('users')->insert([
            'name' => 'Victor Arana',
            'email' => 'victor@codersfree.com',
            'password' => Hash::make('12345678'),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        // Método 2: Usando el modelo
        $user = new \App\Models\User();
        $user->name = 'Iris Godoy';
        $user->email = 'iris@codersfree.com';
        $user->password = Hash::make('12345678');
        $user->save();

        // Método 3: Usando create (requiere fillable)
        \App\Models\User::create([
            'name' => 'Carlos López',
            'email' => 'carlos@codersfree.com',
            'password' => Hash::make('12345678'),
        ]);
    }
}

Seeder para Posts

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class PostSeeder extends Seeder
{
    public function run(): void
    {
        // Crear múltiples posts
        $posts = [
            [
                'title' => 'Post 1',
                'content' => 'Contenido del post 1',
                'category' => 'Tecnología',
                'is_active' => true,
                'publish_at' => now(),
            ],
            [
                'title' => 'Post 2',
                'content' => 'Contenido del post 2',
                'category' => 'Ciencia',
                'is_active' => true,
                'publish_at' => now(),
            ],
            [
                'title' => 'Post 3',
                'content' => 'Contenido del post 3',
                'category' => 'Arte',
                'is_active' => false,
                'publish_at' => null,
            ],
        ];

        foreach ($posts as $post) {
            DB::table('posts')->insert([
                'title' => $post['title'],
                'content' => $post['content'],
                'category' => $post['category'],
                'is_active' => $post['is_active'],
                'publish_at' => $post['publish_at'],
                'created_at' => now(),
                'updated_at' => now(),
            ]);
        }
    }
}

4. DatabaseSeeder: El Orquestador

El DatabaseSeeder es el seeder principal que se encarga de llamar a todos los demás seeders.

DatabaseSeeder básico

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Llamar a los seeders individuales
        $this->call([
            UserSeeder::class,
            PastelSeeder::class,
            PostSeeder::class,
        ]);
    }
}

DatabaseSeeder con orden específico

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // 1. Primero crear roles
        $this->call(RoleSeeder::class);
        
        // 2. Luego usuarios (dependen de roles)
        $this->call(UserSeeder::class);
        
        // 3. Luego categorías
        $this->call(CategorySeeder::class);
        
        // 4. Finalmente posts (dependen de usuarios y categorías)
        $this->call(PostSeeder::class);
    }
}

DatabaseSeeder con datos condicionales

php
<?php

namespace Database\Seeders;

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

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Solo crear si no existe
        if (User::where('email', 'admin@test.com')->doesntExist()) {
            User::create([
                'name' => 'Administrador',
                'email' => 'admin@test.com',
                'password' => bcrypt('admin123'),
            ]);
        }

        // Datos de desarrollo
        if (app()->environment('local')) {
            $this->call([
                TestingSeeder::class,
                DummyDataSeeder::class,
            ]);
        }

        // Datos de producción
        if (app()->environment('production')) {
            $this->call([
                ProductionDataSeeder::class,
            ]);
        }
    }
}

5. Ejecución de Seeders

Comandos principales

bash
# Ejecutar todos los seeders
php artisan db:seed

# Ejecutar un seeder específico
php artisan db:seed --class=UserSeeder

# Ejecutar seeders con migración fresca
php artisan migrate:fresh --seed

# Ejecutar seeders con migración refresh
php artisan migrate:refresh --seed

# Ejecutar seeders en producción (con confirmación)
php artisan db:seed --force

# Ver ayuda
php artisan db:seed --help

Combinaciones comunes

bash
# 1. Resetear todo y poblar
php artisan migrate:fresh --seed

# 2. Solo poblar (sin migrar)
php artisan db:seed

# 3. Poblar un seeder específico
php artisan db:seed --class=PastelSeeder

# 4. En producción
php artisan db:seed --force

Ejecutar seeders en diferentes entornos

bash
# Desarrollo
php artisan db:seed

# Testing
php artisan db:seed --env=testing

# Producción (con confirmación)
php artisan db:seed --force

6. Seeders con Faker

Usar Faker directamente

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Faker\Factory as Faker;
use Illuminate\Support\Facades\DB;

class PastelSeeder extends Seeder
{
    public function run(): void
    {
        $faker = Faker::create('es_ES');

        for ($i = 0; $i < 50; $i++) {
            DB::table('pasteles')->insert([
                'nombre' => 'Pastel ' . $faker->firstNameFemale() . ' ' . $faker->lastName(),
                'sabor' => $faker->randomElement(['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco']),
                'precio' => $faker->randomFloat(2, 50, 500),
                'disponible' => $faker->boolean(80),
                'descripcion' => $faker->sentence(10),
                'created_at' => $faker->dateTimeBetween('-1 year', 'now'),
                'updated_at' => $faker->dateTimeBetween('-1 month', 'now'),
            ]);
        }
    }
}

Faker con datos realistas

php
class PostSeeder extends Seeder
{
    public function run(): void
    {
        $faker = Faker::create('es_ES');

        for ($i = 0; $i < 100; $i++) {
            DB::table('posts')->insert([
                'title' => $faker->sentence(6),
                'content' => $faker->paragraphs(5, true),
                'category' => $faker->randomElement(['Tecnología', 'Ciencia', 'Arte', 'Deportes']),
                'is_active' => $faker->boolean(70),
                'publish_at' => $faker->dateTimeBetween('-1 year', 'now'),
                'created_at' => $faker->dateTimeBetween('-1 year', 'now'),
                'updated_at' => $faker->dateTimeBetween('-1 month', 'now'),
            ]);
        }
    }
}

7. Seeders con Factories

Usando Factories en Seeders

php
<?php

namespace Database\Seeders;

use App\Models\User;
use App\Models\Pastel;
use App\Models\Post;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Crear 10 usuarios con Factory
        User::factory(10)->create();

        // Crear 50 pasteles con Factory
        Pastel::factory(50)->create();

        // Crear 100 posts con Factory
        Post::factory(100)->create();

        // Crear un usuario específico
        User::create([
            'name' => 'Administrador',
            'email' => 'admin@test.com',
            'password' => bcrypt('admin123'),
        ]);

        // Crear pasteles con estados específicos
        Pastel::factory(10)->chocolate()->create();
        Pastel::factory(5)->noDisponible()->create();
        Pastel::factory(3)->caro()->create();
    }
}

Seeder específico con Factory

php
<?php

namespace Database\Seeders;

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

class PastelSeeder extends Seeder
{
    public function run(): void
    {
        // Crear 50 pasteles
        Pastel::factory(50)->create();

        // Crear pasteles de chocolate
        Pastel::factory(10)->chocolate()->create();

        // Crear pasteles no disponibles
        Pastel::factory(5)->noDisponible()->create();

        // Crear pasteles caros
        Pastel::factory(3)->caro()->create();

        // Crear pasteles con datos específicos
        Pastel::factory()->create([
            'nombre' => 'Pastel Especial',
            'sabor' => 'cheesecake',
            'precio' => 299.99,
        ]);
    }
}

8. Orden de Ejecución

Organizar seeders por dependencias

php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // 1. Tablas sin dependencias
        $this->call([
            RoleSeeder::class,        // Roles no dependen de nada
            CategorySeeder::class,    // Categorías no dependen de nada
        ]);

        // 2. Tablas que dependen de otras
        $this->call([
            UserSeeder::class,        // Usuarios dependen de Roles
            ProductSeeder::class,     // Productos dependen de Categorías
        ]);

        // 3. Tablas con relaciones complejas
        $this->call([
            PostSeeder::class,        // Posts dependen de Usuarios
            OrderSeeder::class,       // Órdenes dependen de Usuarios y Productos
        ]);

        // 4. Tablas con relaciones many-to-many
        $this->call([
            ProductTagSeeder::class,  // Productos con Tags
            UserRoleSeeder::class,    // Usuarios con Roles
        ]);
    }
}

Seeders con relaciones

php
// PostSeeder - Depende de usuarios
class PostSeeder extends Seeder
{
    public function run(): void
    {
        $users = User::all();

        foreach ($users as $user) {
            Post::factory(5)->create([
                'user_id' => $user->id
            ]);
        }
    }
}

// Orden en DatabaseSeeder
public function run(): void
{
    $this->call(UserSeeder::class);    // Primero usuarios
    $this->call(PostSeeder::class);    // Luego posts
}

9. Seeders en Producción

Seeders para producción

php
<?php

namespace Database\Seeders;

use App\Models\User;
use App\Models\Role;
use Illuminate\Database\Seeder;

class ProductionSeeder extends Seeder
{
    public function run(): void
    {
        // Crear roles necesarios
        Role::firstOrCreate(['name' => 'admin']);
        Role::firstOrCreate(['name' => 'user']);

        // Crear usuario administrador
        User::firstOrCreate(
            ['email' => 'admin@misitio.com'],
            [
                'name' => 'Administrador',
                'password' => bcrypt('contraseña_segura'),
                'role_id' => Role::where('name', 'admin')->first()->id,
            ]
        );
    }
}

Seeders condicionales por entorno

php
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Siempre ejecutar
        $this->call([
            RoleSeeder::class,
            PermissionSeeder::class,
        ]);

        // Solo en desarrollo
        if (app()->environment('local', 'testing')) {
            $this->call([
                DevelopmentSeeder::class,
                TestDataSeeder::class,
            ]);
        }

        // Solo en producción
        if (app()->environment('production')) {
            $this->call([
                ProductionSeeder::class,
                InitialDataSeeder::class,
            ]);
        }
    }
}

10. Ejemplos Prácticos

Ejemplo 1: Sistema de Blog

php
// UserSeeder.php
<?php

namespace Database\Seeders;

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

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // Usuario administrador
        User::create([
            'name' => 'Admin',
            'email' => 'admin@blog.com',
            'password' => bcrypt('admin123'),
            'role' => 'admin',
        ]);

        // Usuario editor
        User::create([
            'name' => 'Editor',
            'email' => 'editor@blog.com',
            'password' => bcrypt('editor123'),
            'role' => 'editor',
        ]);

        // Usuario normal
        User::create([
            'name' => 'Usuario Normal',
            'email' => 'user@blog.com',
            'password' => bcrypt('user123'),
            'role' => 'user',
        ]);

        // Datos de prueba con Factory
        User::factory(10)->create();
    }
}

// PostSeeder.php
<?php

namespace Database\Seeders;

use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    public function run(): void
    {
        // Posts del usuario administrador
        $admin = User::where('email', 'admin@blog.com')->first();
        Post::factory(10)->create(['user_id' => $admin->id]);

        // Posts del editor
        $editor = User::where('email', 'editor@blog.com')->first();
        Post::factory(20)->create(['user_id' => $editor->id]);

        // Posts de usuarios normales
        User::where('role', 'user')->each(function ($user) {
            Post::factory(5)->create(['user_id' => $user->id]);
        });
    }
}

Ejemplo 2: Sistema de Tienda

php
// CategorySeeder.php
<?php

namespace Database\Seeders;

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

class CategorySeeder extends Seeder
{
    public function run(): void
    {
        $categories = [
            ['name' => 'Electrónicos', 'description' => 'Productos electrónicos'],
            ['name' => 'Ropa', 'description' => 'Prendas de vestir'],
            ['name' => 'Libros', 'description' => 'Libros y publicaciones'],
            ['name' => 'Juguetes', 'description' => 'Juguetes para niños'],
            ['name' => 'Deportes', 'description' => 'Artículos deportivos'],
        ];

        foreach ($categories as $category) {
            Category::create($category);
        }
    }
}

// ProductSeeder.php
<?php

namespace Database\Seeders;

use App\Models\Product;
use App\Models\Category;
use Illuminate\Database\Seeder;

class ProductSeeder extends Seeder
{
    public function run(): void
    {
        // Productos por categoría
        Category::all()->each(function ($category) {
            Product::factory(10)->create([
                'category_id' => $category->id
            ]);
        });

        // Productos específicos
        Product::create([
            'name' => 'Producto Especial',
            'description' => 'Descripción del producto especial',
            'price' => 99.99,
            'stock' => 100,
            'category_id' => Category::first()->id,
        ]);
    }
}

Ejemplo 3: Sistema de Usuarios con Roles

php
// RoleSeeder.php
<?php

namespace Database\Seeders;

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

class RoleSeeder extends Seeder
{
    public function run(): void
    {
        $roles = ['admin', 'editor', 'user', 'guest'];

        foreach ($roles as $role) {
            Role::firstOrCreate(['name' => $role]);
        }
    }
}

// PermissionSeeder.php
<?php

namespace Database\Seeders;

use App\Models\Permission;
use App\Models\Role;
use Illuminate\Database\Seeder;

class PermissionSeeder extends Seeder
{
    public function run(): void
    {
        $permissions = [
            'create-posts',
            'edit-posts',
            'delete-posts',
            'publish-posts',
            'manage-users',
        ];

        foreach ($permissions as $permission) {
            Permission::firstOrCreate(['name' => $permission]);
        }

        // Asignar permisos a roles
        $admin = Role::where('name', 'admin')->first();
        $admin->permissions()->sync(Permission::all());

        $editor = Role::where('name', 'editor')->first();
        $editor->permissions()->sync(
            Permission::whereIn('name', ['create-posts', 'edit-posts'])->get()
        );
    }
}

11. Buenas Prácticas

1. Usar firstOrCreate() para evitar duplicados

php
// ✅ Buena práctica
User::firstOrCreate(
    ['email' => 'admin@test.com'],
    ['name' => 'Admin', 'password' => bcrypt('admin123')]
);

// ❌ Mala práctica
User::create([
    'email' => 'admin@test.com',
    'name' => 'Admin',
    'password' => bcrypt('admin123')
]);

2. Organizar seeders por módulos

php
// DatabaseSeeder.php
public function run(): void
{
    // Módulo de Usuarios
    $this->call([
        RoleSeeder::class,
        UserSeeder::class,
        PermissionSeeder::class,
    ]);

    // Módulo de Contenido
    $this->call([
        CategorySeeder::class,
        TagSeeder::class,
        PostSeeder::class,
    ]);

    // Módulo de Comercio
    $this->call([
        ProductSeeder::class,
        OrderSeeder::class,
    ]);
}

3. Mantener seeders pequeños y específicos

php
// ✅ Cada seeder hace una cosa
UserSeeder::class      // Solo usuarios
PostSeeder::class      // Solo posts
CategorySeeder::class  // Solo categorías

// ❌ Un seeder que hace todo
class EverythingSeeder extends Seeder
{
    public function run(): void
    {
        // Usuarios, posts, categorías, productos, etc.
    }
}

4. Usar factories para datos masivos

php
// ✅ Usar factories para grandes cantidades
User::factory(100)->create();
Post::factory(500)->create();

// ❌ Insertar manualmente muchos registros
for ($i = 0; $i < 100; $i++) {
    DB::table('users')->insert([...]);
}

5. Respetar el orden de dependencias

php
// ✅ Orden correcto (primero lo que no tiene dependencias)
$roles = Role::factory(3)->create();
$users = User::factory(10)->create();
$posts = Post::factory(50)->create();

// ❌ Orden incorrecto (posts depende de users)
$posts = Post::factory(50)->create();
$users = User::factory(10)->create();

6. Usar transacciones para consistencia

php
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        DB::transaction(function () {
            $this->call([
                RoleSeeder::class,
                UserSeeder::class,
                PostSeeder::class,
            ]);
        });
    }
}

7. Crear seeders con datos realistas

php
// ✅ Datos realistas
'name' => 'Victor Arana',
'email' => 'victor@codersfree.com',

// ❌ Datos genéricos
'name' => 'Usuario 1',
'email' => 'user1@test.com',

8. Separar datos de desarrollo de producción

php
class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Siempre ejecutar (producción)
        $this->call(ProductionSeeder::class);

        // Solo en desarrollo
        if (app()->environment('local')) {
            $this->call(DevelopmentSeeder::class);
        }
    }
}

Resumen Rápido

Comandos esenciales

bash
# Crear seeder
php artisan make:seeder NombreSeeder

# Ejecutar todos los seeders
php artisan db:seed

# Ejecutar un seeder específico
php artisan db:seed --class=NombreSeeder

# Migrar y seedear
php artisan migrate:fresh --seed

Estructura de un Seeder

php
<?php

namespace Database\Seeders;

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

class MiSeeder extends Seeder
{
    public function run(): void
    {
        // Datos específicos
        MiModelo::create([
            'campo1' => 'valor1',
            'campo2' => 'valor2',
        ]);

        // Datos masivos
        MiModelo::factory(50)->create();
    }
}

Buenas Prácticas

PrácticaPor qué
Usar firstOrCreate()Evita duplicados
Seeders pequeños y específicosMejor mantenimiento
Respetar dependenciasEvita errores
Usar factoriesCódigo más limpio
Separar por entornosDatos apropiados

Conclusión

Los Seeders son una herramienta esencial en Laravel que te permite:

  1. Mantener datos consistentes durante el desarrollo

  2. Poblar la base de datos automáticamente

  3. Organizar la carga de datos en módulos

  4. Crear datos de prueba realistas con Faker

  5. Combinar con Factories para generación masiva

Recuerda: Un buen seeder es aquel que se ejecuta sin errores y siempre produce los mismos resultados. ¡Practica creando tus propios seeders y verás cómo mejora tu flujo de desarrollo

Comentarios

Entradas más populares de este blog

Tutorial CRUD con Laravel 12-Pastel::factory(50)->create();

CRUD Completo con Laravel 12 - Guía Práctica Paso a Paso-productos

CRUD en Laravel 12 con Git - Guía para Principiantes