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
¿Qué son los Seeders?
Estructura de un Seeder
Creación de Seeders
DatabaseSeeder: El Orquestador
Ejecución de Seeders
Seeders con Faker
Seeders con Factories
Orden de Ejecución
Seeders en Producción
Ejemplos Prácticos
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?
# 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/:
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
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
# 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=UserSeeder para Usuarios
<?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
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
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
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
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
# 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 --helpCombinaciones comunes
# 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 --forceEjecutar seeders en diferentes entornos
# Desarrollo
php artisan db:seed
# Testing
php artisan db:seed --env=testing
# Producción (con confirmación)
php artisan db:seed --force6. Seeders con Faker
Usar Faker directamente
<?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
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
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
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
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
// 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
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
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
// 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
// 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
// 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
// ✅ 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
// 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
// ✅ 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
// ✅ 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
// ✅ 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
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
// ✅ 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
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
# 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 --seedEstructura de un Seeder
<?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áctica | Por qué |
|---|---|
Usar firstOrCreate() | Evita duplicados |
| Seeders pequeños y específicos | Mejor mantenimiento |
| Respetar dependencias | Evita errores |
| Usar factories | Código más limpio |
| Separar por entornos | Datos apropiados |
Conclusión
Los Seeders son una herramienta esencial en Laravel que te permite:
Mantener datos consistentes durante el desarrollo
Poblar la base de datos automáticamente
Organizar la carga de datos en módulos
Crear datos de prueba realistas con Faker
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
Publicar un comentario