8: Model Factories en Laravel 12 - Guía Práctica
Introducción
Los Model Factories son una herramienta poderosa en Laravel que te permite generar datos de prueba de manera rápida y consistente. Utilizan el componente Faker para crear datos realistas y te ayudan a poblar tu base de datos para pruebas, desarrollo y demostraciones. En este tutorial, aprenderás todo sobre Model Factories en Laravel 12.
Índice
¿Qué son los Model Factories?
Configuración Inicial
Creación de Factories
Tipos de Factories
Relaciones entre Factories
Métodos de Factory
Uso en Seeders
Factories para Pruebas
Personalización Avanzada
Ejercicio Práctico: Sistema de Pastelería
1. ¿Qué son los Model Factories?
Los Model Factories son clases que definen cómo crear instancias de tus modelos con datos de prueba. Trabajan con el componente Faker para generar datos realistas y te permiten:
Generar grandes cantidades de datos de prueba
Mantener la consistencia en tus datos
Definir diferentes "estados" para tus modelos
Crear relaciones entre modelos fácilmente
Estructura básica de un Factory
<?php
namespace Database\Factories;
use App\Models\Pastel;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
return [
'nombre' => $this->faker->name(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla', 'cheesecake']),
'precio' => $this->faker->randomFloat(2, 10, 100),
'created_at' => now(),
'updated_at' => now(),
];
}
}2. Configuración Inicial
Instalación y verificación
# Laravel 12 ya incluye los factories por defecto
# Verificar que está instalado en composer.json
composer require --dev laravel/legacy-factories
# Crear un nuevo proyecto con factories
composer create-project laravel/laravel pasteleria-appEstructura de carpetas
database/
├── factories/
│ ├── PastelFactory.php
│ ├── UserFactory.php
│ └── ...
├── migrations/
│ └── ...
└── seeders/
├── DatabaseSeeder.php
└── ...Archivo de configuración
// config/app.php
'faker_locale' => 'es_ES', // Para datos en español3. Creación de Factories
Comando para crear un Factory
# Crear factory para un modelo específico
php artisan make:factory PastelFactory --model=Pastel
# Crear factory sin modelo específico
php artisan make:factory PastelFactory
# Crear factory con modelo y migración
php artisan make:factory PastelFactory --model=Pastel --migrationFactory básico
<?php
namespace Database\Factories;
use App\Models\Pastel;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName() . ' ' . $this->faker->lastName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco']),
'precio' => $this->faker->randomFloat(2, 50, 500),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->faker->sentence(10),
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
'updated_at' => $this->faker->dateTimeBetween('-1 month', 'now'),
];
}
}Factory con datos personalizados
<?php
namespace Database\Factories;
use App\Models\Pastel;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
// Definir sabores disponibles
$sabores = ['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'];
$sabor = $this->faker->randomElement($sabores);
return [
'nombre' => $this->generarNombre($sabor),
'sabor' => $sabor,
'precio' => $this->generarPrecio($sabor),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->generarDescripcion($sabor),
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
'updated_at' => $this->faker->dateTimeBetween('-1 month', 'now'),
];
}
private function generarNombre(string $sabor): string
{
$prefijos = ['Delicioso', 'Exquisito', 'Especial', 'Fabuloso', 'Increíble'];
$sufijos = ['Pastel', 'Torta', 'Bocado', 'Dulce'];
return $this->faker->randomElement($prefijos) . ' ' .
ucfirst($sabor) . ' ' .
$this->faker->randomElement($sufijos);
}
private function generarPrecio(string $sabor): float
{
$preciosBase = [
'chocolate' => [80, 200],
'vainilla' => [60, 150],
'cheesecake' => [120, 300],
'fresa' => [70, 180],
'coco' => [90, 220]
];
list($min, $max) = $preciosBase[$sabor] ?? [50, 200];
return $this->faker->randomFloat(2, $min, $max);
}
private function generarDescripcion(string $sabor): string
{
$descripciones = [
'chocolate' => 'Un exquisito pastel de chocolate con cobertura de ganache.',
'vainilla' => 'Delicado pastel de vainilla con crema de mantequilla.',
'cheesecake' => 'Cremoso cheesecake con base de galleta y topping de fresa.',
'fresa' => 'Pastel de fresa con relleno de crema y fresas naturales.',
'coco' => 'Pastel de coco con crema de coco y coco rallado.'
];
return $descripciones[$sabor] ?? $this->faker->sentence(10);
}
}4. Tipos de Factories
Factory con estados (States)
Los estados te permiten definir variaciones de tu factory:
<?php
namespace Database\Factories;
use App\Models\Pastel;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla', 'cheesecake']),
'precio' => $this->faker->randomFloat(2, 50, 500),
'disponible' => true,
'descripcion' => $this->faker->sentence(10),
];
}
/**
* Indicar que el pastel está disponible
*/
public function disponible(): static
{
return $this->state(fn (array $attributes) => [
'disponible' => true,
]);
}
/**
* Indicar que el pastel no está disponible
*/
public function noDisponible(): static
{
return $this->state(fn (array $attributes) => [
'disponible' => false,
]);
}
/**
* Indicar que el pastel es de chocolate
*/
public function chocolate(): static
{
return $this->state(fn (array $attributes) => [
'sabor' => 'chocolate',
'precio' => $this->faker->randomFloat(2, 80, 300),
'descripcion' => 'Delicioso pastel de chocolate con ganache',
]);
}
/**
* Indicar que el pastel es de vainilla
*/
public function vainilla(): static
{
return $this->state(fn (array $attributes) => [
'sabor' => 'vainilla',
'precio' => $this->faker->randomFloat(2, 60, 250),
'descripcion' => 'Exquisito pastel de vainilla con crema de mantequilla',
]);
}
/**
* Indicar que el pastel es caro
*/
public function caro(): static
{
return $this->state(fn (array $attributes) => [
'precio' => $this->faker->randomFloat(2, 200, 500),
]);
}
/**
* Indicar que el pastel es económico
*/
public function economico(): static
{
return $this->state(fn (array $attributes) => [
'precio' => $this->faker->randomFloat(2, 30, 80),
]);
}
}Factory con secuencias
public function definition(): array
{
return [
'nombre' => $this->faker->name(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla']),
'precio' => $this->faker->randomFloat(2, 10, 100),
'disponible' => $this->faker->boolean(),
];
}
// Uso con secuencia
Pastel::factory()
->count(10)
->sequence(
['sabor' => 'chocolate'],
['sabor' => 'vainilla'],
['sabor' => 'cheesecake'],
)
->create();
// Secuencia con función
Pastel::factory()
->count(5)
->sequence(fn ($sequence) => [
'nombre' => 'Pastel ' . ($sequence->index + 1),
'precio' => 50 + ($sequence->index * 10),
])
->create();Factory con relaciones
<?php
namespace Database\Factories;
use App\Models\Pastel;
use App\Models\Categoria;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla', 'cheesecake']),
'precio' => $this->faker->randomFloat(2, 50, 500),
'categoria_id' => Categoria::factory(),
'disponible' => true,
'descripcion' => $this->faker->sentence(10),
];
}
/**
* Relación con una categoría específica
*/
public function categoria(Categoria $categoria): static
{
return $this->state(fn (array $attributes) => [
'categoria_id' => $categoria->id,
]);
}
/**
* Relación con múltiples ingredientes
*/
public function withIngredientes(int $count = 3): static
{
return $this->afterCreating(function (Pastel $pastel) use ($count) {
$pastel->ingredientes()->attach(
Ingrediente::factory($count)->create()
);
});
}
}5. Relaciones entre Factories
Relación uno a muchos
// Factory de Categoría
class CategoriaFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => $this->faker->word(),
'descripcion' => $this->faker->sentence(),
];
}
public function withPasteles(int $count = 3): static
{
return $this->has(Pastel::factory($count));
}
}
// Factory de Pastel con categoría
class PastelFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla']),
'categoria_id' => Categoria::factory(),
];
}
}Relación muchos a muchos
class PastelFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla']),
];
}
public function withIngredientes(int $count = 3): static
{
return $this->afterCreating(function (Pastel $pastel) use ($count) {
$ingredientes = Ingrediente::factory($count)->create();
$pastel->ingredientes()->attach($ingredientes);
});
}
}
class IngredienteFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => $this->faker->word(),
'cantidad' => $this->faker->randomFloat(2, 1, 10),
'unidad' => $this->faker->randomElement(['g', 'kg', 'ml', 'L', 'unidad']),
];
}
}Relación recursiva
class PastelFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla']),
'parent_id' => null, // Por defecto sin padre
];
}
public function conSubpasteles(int $count = 2): static
{
return $this->has(Pastel::factory($count), 'subpasteles');
}
public function subpastel(): static
{
return $this->state(fn (array $attributes) => [
'parent_id' => Pastel::factory(),
]);
}
}6. Métodos de Factory
Métodos principales
// Crear una instancia sin guardar en BD
$pastel = Pastel::factory()->make();
// Crear una instancia y guardarla en BD
$pastel = Pastel::factory()->create();
// Crear múltiples instancias y guardarlas
$pasteles = Pastel::factory()->count(10)->create();
// Crear con datos específicos
$pastel = Pastel::factory()->create([
'nombre' => 'Pastel Especial',
'precio' => 150.00
]);
// Crear con estado específico
$pastel = Pastel::factory()->chocolate()->create();
// Crear con múltiples estados
$pastel = Pastel::factory()->chocolate()->disponible()->create();
// Crear y devolver la instancia sin persistir
$pastel = Pastel::factory()->make([
'precio' => 200
]);
// Crear y persistir múltiples instancias
Pastel::factory()->count(5)->create();
// Crear y forzar campos específicos
$pastel = Pastel::factory()->state([
'disponible' => false
])->create();
// Crear y ejecutar callbacks después de crear
$pastel = Pastel::factory()->afterCreating(function ($pastel) {
$pastel->ingredientes()->attach([1, 2, 3]);
})->create();Métodos para relaciones
// Crear con relación hasMany
$categoria = Categoria::factory()
->has(Pastel::factory()->count(5))
->create();
// Crear con relación belongsTo
$pastel = Pastel::factory()
->for(Categoria::factory())
->create();
// Crear con relación hasOne
$pastel = Pastel::factory()
->has(Detalle::factory())
->create();
// Crear con relación belongsToMany
$pastel = Pastel::factory()
->hasAttached(Ingrediente::factory()->count(3))
->create();
// Crear con relación y datos específicos
$pastel = Pastel::factory()
->hasAttached(
Ingrediente::factory()->count(3),
['cantidad' => 100, 'unidad' => 'g']
)
->create();Métodos de configuración
// Crear con secuencia
Pastel::factory()
->count(10)
->sequence(
['sabor' => 'chocolate', 'precio' => 100],
['sabor' => 'vainilla', 'precio' => 80],
['sabor' => 'cheesecake', 'precio' => 150]
)
->create();
// Crear con callback por cada iteración
Pastel::factory()
->count(10)
->sequence(fn ($sequence) => [
'nombre' => 'Pastel ' . ($sequence->index + 1),
'precio' => 50 + ($sequence->index * 10),
])
->create();
// Crear con afterMaking y afterCreating
Pastel::factory()
->afterMaking(function ($pastel) {
// Log antes de crear
Log::info('Creando pastel: ' . $pastel->nombre);
})
->afterCreating(function ($pastel) {
// Log después de crear
Log::info('Pastel creado: ' . $pastel->id);
})
->create();7. Uso en Seeders
DatabaseSeeder básico
<?php
namespace Database\Seeders;
use App\Models\Pastel;
use App\Models\User;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Crear pasteles con factory
Pastel::factory(50)->create();
// Crear usuarios
User::factory(10)->create();
// Crear pasteles con estados específicos
Pastel::factory(10)->chocolate()->create();
Pastel::factory(10)->vainilla()->create();
Pastel::factory(5)->noDisponible()->create();
Pastel::factory(5)->caro()->create();
}
}Seeders específicos
# Crear un seeder específico
php artisan make:seeder PastelSeeder<?php
namespace Database\Seeders;
use App\Models\Pastel;
use App\Models\Categoria;
use Illuminate\Database\Seeder;
class PastelSeeder extends Seeder
{
public function run(): void
{
// Crear categorías
$categorias = Categoria::factory(5)->create();
// Crear pasteles para cada categoría
foreach ($categorias as $categoria) {
Pastel::factory(10)
->categoria($categoria)
->create();
}
// Crear pasteles especiales
Pastel::factory(3)
->chocolate()
->disponible()
->caro()
->create();
// Crear pasteles económicos
Pastel::factory(10)
->economico()
->create();
// Crear pasteles con ingredientes
Pastel::factory(5)
->withIngredientes(5)
->create();
}
}Registrar seeders
// database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
CategoriaSeeder::class,
PastelSeeder::class,
UserSeeder::class,
IngredienteSeeder::class,
]);
}
}Ejecutar seeders
# Ejecutar todos los seeders
php artisan db:seed
# Ejecutar un seeder específico
php artisan db:seed --class=PastelSeeder
# Ejecutar con migración fresca
php artisan migrate:fresh --seed
# Ejecutar con opción de fuerza
php artisan db:seed --force
# Ejecutar en entorno de testing
php artisan db:seed --env=testing8. Factories para Pruebas
Usar factories en tests
<?php
namespace Tests\Feature;
use App\Models\Pastel;
use Tests\TestCase;
class PastelControllerTest extends TestCase
{
public function test_crear_pastel()
{
$data = Pastel::factory()->make()->toArray();
$response = $this->post('/pasteles', $data);
$response->assertStatus(201);
$this->assertDatabaseHas('pasteles', [
'nombre' => $data['nombre']
]);
}
public function test_ver_pastel()
{
$pastel = Pastel::factory()->create();
$response = $this->get("/pasteles/{$pastel->id}");
$response->assertStatus(200);
$response->assertSee($pastel->nombre);
}
public function test_listar_pasteles()
{
Pastel::factory(10)->create();
$response = $this->get('/pasteles');
$response->assertStatus(200);
$response->assertViewHas('pasteles', function ($pasteles) {
return $pasteles->count() === 10;
});
}
}Factories en tests de API
public function test_api_listar_pasteles()
{
$pasteles = Pastel::factory(5)
->chocolate()
->create();
$response = $this->getJson('/api/pasteles');
$response->assertStatus(200)
->assertJsonCount(5, 'data')
->assertJsonStructure([
'data' => [
'*' => ['id', 'nombre', 'sabor', 'precio']
]
]);
}
public function test_api_crear_pastel()
{
$data = Pastel::factory()->make()->toArray();
$response = $this->postJson('/api/pasteles', $data);
$response->assertStatus(201)
->assertJson([
'success' => true,
'message' => 'Pastel creado exitosamente'
]);
$this->assertDatabaseHas('pasteles', $data);
}9. Personalización Avanzada
Factory con dependencias
class PastelFactory extends Factory
{
protected $model = Pastel::class;
private function obtenerPrecioPorSabor(string $sabor): float
{
$precios = [
'chocolate' => [80, 200],
'vainilla' => [60, 150],
'cheesecake' => [120, 300],
];
list($min, $max) = $precios[$sabor] ?? [50, 200];
return $this->faker->randomFloat(2, $min, $max);
}
public function definition(): array
{
$sabor = $this->faker->randomElement(['chocolate', 'vainilla', 'cheesecake']);
return [
'nombre' => $this->generarNombre($sabor),
'sabor' => $sabor,
'precio' => $this->obtenerPrecioPorSabor($sabor),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->generarDescripcion($sabor),
];
}
private function generarNombre(string $sabor): string
{
$adjetivos = ['Delicioso', 'Exquisito', 'Increíble', 'Especial'];
$nombres = ['Pastel', 'Torta', 'Bocado'];
return $this->faker->randomElement($adjetivos) . ' ' .
ucfirst($sabor) . ' ' .
$this->faker->randomElement($nombres);
}
private function generarDescripcion(string $sabor): string
{
$descripciones = [
'chocolate' => 'Un exquisito pastel de chocolate con cobertura de ganache',
'vainilla' => 'Delicado pastel de vainilla con crema de mantequilla',
'cheesecake' => 'Cremoso cheesecake con base de galleta y topping de fresa',
];
return $descripciones[$sabor] ?? $this->faker->sentence(10);
}
}Factory con callbacks
class PastelFactory extends Factory
{
public function definition(): array
{
return [
'nombre' => 'Pastel ' . $this->faker->firstName(),
'sabor' => $this->faker->randomElement(['chocolate', 'vainilla']),
'precio' => $this->faker->randomFloat(2, 50, 500),
];
}
// After making callback
public function afterMaking()
{
return $this->state(function (array $attributes) {
// Modificar atributos antes de crear
return [
'nombre' => $this->faker->unique()->firstName() . ' Pastel'
];
});
}
// After creating callback
public function afterCreating()
{
return $this->afterCreating(function (Pastel $pastel) {
// Ejecutar después de crear
$pastel->update([
'codigo' => 'PASTEL-' . str_pad($pastel->id, 4, '0', STR_PAD_LEFT)
]);
});
}
}Factory para usuarios con roles
class UserFactory extends Factory
{
public function definition(): array
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'password' => bcrypt('password'),
'remember_token' => Str::random(10),
];
}
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'role' => 'admin',
]);
}
public function encargado(): static
{
return $this->state(fn (array $attributes) => [
'role' => 'encargado',
]);
}
public function cliente(): static
{
return $this->state(fn (array $attributes) => [
'role' => 'cliente',
]);
}
}
// Uso en seeder
User::factory(10)->admin()->create();
User::factory(5)->encargado()->create();
User::factory(20)->cliente()->create();10. Ejercicio Práctico: Sistema de Pastelería
Paso 1: Crear modelos y migraciones
# Crear modelos con migraciones
php artisan make:model Pastel -m
php artisan make:model Categoria -m
php artisan make:model Ingrediente -m
php artisan make:model Venta -mPaso 2: Crear factories
php artisan make:factory PastelFactory --model=Pastel
php artisan make:factory CategoriaFactory --model=Categoria
php artisan make:factory IngredienteFactory --model=Ingrediente
php artisan make:factory VentaFactory --model=VentaPaso 3: Implementar factories
// database/factories/CategoriaFactory.php
<?php
namespace Database\Factories;
use App\Models\Categoria;
use Illuminate\Database\Eloquent\Factories\Factory;
class CategoriaFactory extends Factory
{
protected $model = Categoria::class;
public function definition(): array
{
$categorias = ['Tortas', 'Pasteles', 'Galletas', 'Brownies', 'Cupcakes'];
return [
'nombre' => $this->faker->randomElement($categorias),
'descripcion' => $this->faker->sentence(6),
];
}
}
// database/factories/PastelFactory.php
<?php
namespace Database\Factories;
use App\Models\Pastel;
use App\Models\Categoria;
use Illuminate\Database\Eloquent\Factories\Factory;
class PastelFactory extends Factory
{
protected $model = Pastel::class;
public function definition(): array
{
$sabores = ['chocolate', 'vainilla', 'cheesecake', 'fresa', 'coco'];
$sabor = $this->faker->randomElement($sabores);
return [
'nombre' => $this->generarNombre($sabor),
'sabor' => $sabor,
'precio' => $this->generarPrecio($sabor),
'disponible' => $this->faker->boolean(80),
'descripcion' => $this->generarDescripcion($sabor),
'categoria_id' => Categoria::factory(),
'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
'updated_at' => $this->faker->dateTimeBetween('-1 month', 'now'),
];
}
private function generarNombre(string $sabor): string
{
$prefijos = ['Delicioso', 'Exquisito', 'Especial', 'Increíble', 'Fabuloso'];
return $this->faker->randomElement($prefijos) . ' ' .
ucfirst($sabor) . ' ' .
$this->faker->randomElement(['Pastel', 'Torta', 'Bocado']);
}
private function generarPrecio(string $sabor): float
{
$precios = [
'chocolate' => [80, 200],
'vainilla' => [60, 150],
'cheesecake' => [120, 300],
'fresa' => [70, 180],
'coco' => [90, 220]
];
list($min, $max) = $precios[$sabor] ?? [50, 200];
return $this->faker->randomFloat(2, $min, $max);
}
private function generarDescripcion(string $sabor): string
{
$descripciones = [
'chocolate' => 'Exquisito pastel de chocolate con cobertura de ganache',
'vainilla' => 'Delicado pastel de vainilla con crema de mantequilla',
'cheesecake' => 'Cremoso cheesecake con base de galleta y topping de fresa',
'fresa' => 'Pastel de fresa con relleno de crema y fresas naturales',
'coco' => 'Pastel de coco con crema de coco y coco rallado'
];
return $descripciones[$sabor] ?? $this->faker->sentence(10);
}
// Estados
public function chocolate(): static
{
return $this->state(fn (array $attributes) => [
'sabor' => 'chocolate',
'precio' => $this->faker->randomFloat(2, 80, 300),
]);
}
public function disponible(): static
{
return $this->state(fn (array $attributes) => [
'disponible' => true,
]);
}
public function noDisponible(): static
{
return $this->state(fn (array $attributes) => [
'disponible' => false,
]);
}
public function caro(): static
{
return $this->state(fn (array $attributes) => [
'precio' => $this->faker->randomFloat(2, 200, 500),
]);
}
// Relaciones
public function categoria(Categoria $categoria): static
{
return $this->state(fn (array $attributes) => [
'categoria_id' => $categoria->id,
]);
}
public function withIngredientes(int $count = 3): static
{
return $this->afterCreating(function (Pastel $pastel) use ($count) {
$ingredientes = Ingrediente::factory($count)->create();
$pastel->ingredientes()->attach($ingredientes);
});
}
}
// database/factories/IngredienteFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class IngredienteFactory extends Factory
{
public function definition(): array
{
$ingredientes = ['Harina', 'Huevo', 'Azúcar', 'Mantequilla', 'Chocolate', 'Vainilla', 'Crema', 'Fresa'];
return [
'nombre' => $this->faker->randomElement($ingredientes),
'cantidad' => $this->faker->randomFloat(2, 1, 10),
'unidad' => $this->faker->randomElement(['g', 'kg', 'ml', 'L', 'unidad']),
'precio_unitario' => $this->faker->randomFloat(2, 1, 50),
];
}
}
// database/factories/VentaFactory.php
<?php
namespace Database\Factories;
use App\Models\Pastel;
use Illuminate\Database\Eloquent\Factories\Factory;
class VentaFactory extends Factory
{
public function definition(): array
{
return [
'pastel_id' => Pastel::factory(),
'cantidad' => $this->faker->numberBetween(1, 10),
'precio_total' => $this->faker->randomFloat(2, 50, 1000),
'cliente_nombre' => $this->faker->name(),
'cliente_email' => $this->faker->email(),
'fecha_venta' => $this->faker->dateTimeBetween('-6 months', 'now'),
'estado' => $this->faker->randomElement(['pendiente', 'pagado', 'entregado', 'cancelado']),
];
}
public function pagado(): static
{
return $this->state(fn (array $attributes) => [
'estado' => 'pagado',
]);
}
public function entregado(): static
{
return $this->state(fn (array $attributes) => [
'estado' => 'entregado',
]);
}
}Paso 4: Crear seeders
php artisan make:seeder CategoriaSeeder
php artisan make:seeder PastelSeeder
php artisan make:seeder IngredienteSeeder
php artisan make:seeder VentaSeeder// database/seeders/CategoriaSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Categoria;
use Illuminate\Database\Seeder;
class CategoriaSeeder extends Seeder
{
public function run(): void
{
Categoria::factory(5)->create();
}
}
// database/seeders/PastelSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Pastel;
use App\Models\Categoria;
use Illuminate\Database\Seeder;
class PastelSeeder extends Seeder
{
public function run(): void
{
// Crear pasteles para cada categoría
Categoria::all()->each(function ($categoria) {
Pastel::factory(10)
->categoria($categoria)
->disponible()
->create();
});
// Crear pasteles especiales
Pastel::factory(3)
->chocolate()
->caro()
->withIngredientes(5)
->create();
// Crear pasteles no disponibles
Pastel::factory(5)
->noDisponible()
->create();
}
}
// database/seeders/IngredienteSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Ingrediente;
use Illuminate\Database\Seeder;
class IngredienteSeeder extends Seeder
{
public function run(): void
{
Ingrediente::factory(20)->create();
}
}
// database/seeders/VentaSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Venta;
use App\Models\Pastel;
use Illuminate\Database\Seeder;
class VentaSeeder extends Seeder
{
public function run(): void
{
// Crear ventas para pasteles disponibles
Pastel::where('disponible', true)->each(function ($pastel) {
Venta::factory(5)
->pagado()
->create([
'pastel_id' => $pastel->id,
'precio_total' => $pastel->precio * $this->faker->numberBetween(1, 3)
]);
});
// Crear algunas ventas entregadas
Venta::factory(10)
->entregado()
->create();
}
}Paso 5: Actualizar DatabaseSeeder
// database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
CategoriaSeeder::class,
IngredienteSeeder::class,
PastelSeeder::class,
VentaSeeder::class,
]);
}
}Paso 6: Ejecutar los seeders
# Resetear y ejecutar todos los seeders
php artisan migrate:fresh --seed
# O ejecutar solo el seeder de pasteles
php artisan db:seed --class=PastelSeederComandos Útiles
# Crear un nuevo factory
php artisan make:factory NombreFactory --model=Modelo
# Crear factory sin modelo
php artisan make:factory NombreFactory
# Crear factory con modelo y migración
php artisan make:factory NombreFactory --model=Modelo --migration
# Ejecutar seeders
php artisan db:seed
# Ejecutar un seeder específico
php artisan db:seed --class=NombreSeeder
# Resetear y ejecutar seeders
php artisan migrate:fresh --seed
# Ver los seeders disponibles
php artisan db:seed --helpResumen
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Factory | Genera datos de prueba | PastelFactory |
| State | Variaciones del factory | chocolate(), disponible() |
| Relations | Crea relaciones | has(), for(), hasAttached() |
| Count | Número de registros | count(10) |
| Make | Crea sin persistir | make() |
| Create | Crea y persiste | create() |
| Sequence | Secuencia de datos | sequence(...) |
| Callback | Ejecutar código | afterCreating() |
¡Con los Model Factories puedes generar datos de prueba de manera eficiente y mantener tu base de datos poblada para desarrollo y pruebas!
Comentarios
Publicar un comentario