1. Eloquent ORM
  2. Eloquent: Fábricas

Únete a nuestra comunidad de Telegram @webblend!

Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.

Introducción

Cuando estás probando tu aplicación o poblando tu base de datos, es posible que necesites insertar algunos registros en tu base de datos. En lugar de especificar manualmente el valor de cada columna, Laravel te permite definir un conjunto de atributos predeterminados para cada uno de tus modelos Eloquent mediante fábricas de modelos.

Para ver un ejemplo de cómo escribir una fábrica, echa un vistazo al archivo database/factories/UserFactory.php en tu aplicación. Esta fábrica se incluye en todas las nuevas aplicaciones de Laravel y contiene la siguiente definición de fábrica:

namespace Database\Factories;
 
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
 
class UserFactory extends Factory
{
/**
* Define el estado predeterminado del modelo.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
}

Como puedes ver, en su forma más básica, las fábricas son clases que extienden la clase base de fábrica de Laravel y definen un método definition. El método definition devuelve el conjunto predeterminado de valores de atributos que se deben aplicar al crear un modelo utilizando la fábrica.

A través del ayudante fake, las fábricas tienen acceso a la biblioteca Faker de PHP, que te permite generar de manera conveniente varios tipos de datos aleatorios para pruebas y llenado de datos.

Nota Puedes establecer la configuración regional de Faker de tu aplicación añadiendo una opción faker_locale en tu archivo de configuración config/app.php.

Definición de Fábricas de Modelos

Generación de Fábricas

Para crear una fábrica, ejecuta el comando Artisan make:factory Artisan:

php artisan make:factory PostFactory

La nueva clase de fábrica se colocará en tu directorio database/factories.

Convenciones de Descubrimiento de Modelos y Fábricas

Una vez que hayas definido tus fábricas, puedes usar el método estático factory proporcionado a tus modelos por el rasgo Illuminate\Database\Eloquent\Factories\HasFactory para instanciar una instancia de fábrica para ese modelo.

El método factory del rasgo HasFactory utilizará convenciones para determinar la fábrica adecuada para el modelo al que se asigna el rasgo. Específicamente, el método buscará una fábrica en el espacio de nombres Database\Factories que tenga un nombre de clase que coincida con el nombre del modelo y que tenga como sufijo Factory. Si estas convenciones no se aplican a tu aplicación o fábrica en particular, puedes sobrescribir el método newFactory en tu modelo para devolver directamente una instancia de la fábrica correspondiente al modelo:

use Illuminate\Database\Eloquent\Factories\Factory;
use Database\Factories\Administration\FlightFactory;
 
/**
* Crea una nueva instancia de fábrica para el modelo.
*/
protected static function newFactory(): Factory
{
return FlightFactory::new();
}

Luego, define una propiedad model en la fábrica correspondiente:

use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;
 
class FlightFactory extends Factory
{
/**
* El nombre del modelo correspondiente a la fábrica.
*
* @var class-string<\Illuminate\Database\Eloquent\Model>
*/
protected $model = Flight::class;
}

Estados de Fábrica

Los métodos de manipulación de estados te permiten definir modificaciones discretas que se pueden aplicar a tus fábricas de modelos en cualquier combinación. Por ejemplo, tu fábrica Database\Factories\UserFactory podría contener un método de estado suspended que modifica uno de sus valores predeterminados de atributo.

Los métodos de transformación de estados suelen llamar al método state proporcionado por la clase de fábrica base de Laravel. El método state acepta un cierre que recibirá el conjunto de atributos sin procesar definido para la fábrica y debería devolver un conjunto de atributos a modificar:

use Illuminate\Database\Eloquent\Factories\Factory;
 
/**
* Indica que el usuario está suspendido.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
});
}

Estado "Eliminado"

Si tu modelo Eloquent puede ser eliminado suavemente, puedes invocar el método de estado integrado trashed para indicar que el modelo creado ya debe estar "eliminado suavemente". No es necesario definir manualmente el estado trashed, ya que está disponible automáticamente para todas las fábricas:

use App\Models\User;
 
$user = User::factory()->trashed()->create();

Funciones de Retorno de Fábrica

Las funciones de retorno de fábrica se registran mediante los métodos afterMaking y afterCreating y te permiten realizar tareas adicionales después de hacer o crear un modelo. Debes registrar estas funciones de retorno definiendo un método configure en tu clase de fábrica. Este método será llamado automáticamente por Laravel cuando se instancie la fábrica:

namespace Database\Factories;
 
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
 
class UserFactory extends Factory
{
/**
* Configura la fábrica del modelo.
*/
public function configure(): static
{
return $this->afterMaking(function (User $user) {
// ...
})->afterCreating(function (User $user) {
// ...
});
}
 
// ...
}

También puedes registrar funciones de retorno de fábrica dentro de los métodos de estado para realizar tareas adicionales que sean específicas de un estado dado:

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
 
/**
* Indica que el usuario está suspendido.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
})->afterMaking(function (User $user) {
// ...
})->afterCreating(function (User $user) {
// ...
});
}

Creación de Modelos Usando Fábricas

Instanciación de Modelos

Una vez que hayas definido tus fábricas, puedes usar el método estático factory proporcionado a tus modelos por el rasgo Illuminate\Database\Eloquent\Factories\HasFactory para instanciar una instancia de fábrica para ese modelo. Veamos algunos ejemplos de creación de modelos. Primero, usaremos el método make para crear modelos sin persistirlos en la base de datos:

use App\Models\User;
 
$user = User::factory()->make();

Puedes crear una colección de muchos modelos usando el método count:

$users = User::factory()->count(3)->make();

Aplicar Estados

También puedes aplicar cualquiera de tus estados a los modelos. Si deseas aplicar múltiples transformaciones de estado a los modelos, simplemente puedes llamar a los métodos de transformación de estado directamente:

$users = User::factory()->count(5)->suspended()->make();

Anulación de Atributos

Si deseas anular algunos de los valores predeterminados de tus modelos, puedes pasar un conjunto de valores al método make. Solo los atributos especificados se reemplazarán mientras que el resto de los atributos permanecerán configurados con sus valores predeterminados especificados por la fábrica:

$user = User::factory()->make([
'name' => 'Abigail Otwell',
]);

Alternativamente, el método state se puede llamar directamente en la instancia de la fábrica para realizar una transformación de estado en línea:

$user = User::factory()->state([
'name' => 'Abigail Otwell',
])->make();

Nota La protección de asignación masiva se desactiva automáticamente al crear modelos mediante fábricas.

Persistencia de Modelos

El método create instancia modelos y los persiste en la base de datos utilizando el método save de Eloquent:

use App\Models\User;
 
// Crea una instancia única de App\Models\User...
$user = User::factory()->create();
 
// Crea tres instancias de App\Models\User...
$users = User::factory()->count(3)->create();

Puedes anular los atributos predeterminados de la fábrica pasando un conjunto de atributos al método create:

$user = User::factory()->create([
'name' => 'Abigail',
]);

Secuencias

A veces, es posible que desees alternar el valor de un determinado atributo del modelo para cada modelo creado. Puedes lograr esto definiendo una transformación de estado como una secuencia. Por ejemplo, es posible que desees alternar el valor de una columna admin entre Y y N para cada usuario creado:

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
 
$users = User::factory()
->count(10)
->state(new Sequence(
['admin' => 'Y'],
['admin' => 'N'],
))
->create();

En este ejemplo, se crearán cinco usuarios con un valor admin de Y y cinco usuarios con un valor admin de N.

Si es necesario, puedes incluir un cierre como un valor de secuencia. El cierre se invocará cada vez que la secuencia necesite un nuevo valor:

use Illuminate\Database\Eloquent\Factories\Sequence;
 
$users = User::factory()
->count(10)
->state(new Sequence(
fn (Sequence $sequence) => ['role' => UserRoles::all()->random()],
))
->create();

Dentro de un cierre de secuencia, puedes acceder a las propiedades $index o $count en la instancia de la secuencia que se inyecta en el cierre. La propiedad $index contiene el número de iteraciones a través de la secuencia que se han producido hasta ahora, mientras que la propiedad $count contiene el número total de veces que se invocará la secuencia:

$users = User::factory()
->count(10)
->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index])
->create();

Para mayor comodidad, las secuencias también se pueden aplicar mediante el método sequence, que simplemente invoca internamente el método state. El método sequence acepta un cierre o arreglos de atributos secuenciados:

$users = User::factory()
->count(2)
->sequence(
['name' => 'First User'],
['name' => 'Second User'],
)
->create();

Relaciones de Fábrica

Relaciones Has Many

A continuación, exploremos la construcción de relaciones de modelos Eloquent utilizando los métodos de fábrica fluidos de Laravel. Primero, supongamos que nuestra aplicación tiene un modelo App\Models\User y un modelo App\Models\Post. Además, supongamos que el modelo User define una relación hasMany con Post. Podemos crear un usuario que tenga tres publicaciones usando el método has proporcionado por las fábricas de Laravel. El método has acepta una instancia de fábrica:

use App\Models\Post;
use App\Models\User;
 
$user = User::factory()
->has(Post::factory()->count(3))
->create();

Por convención, al pasar un modelo Post al método has, Laravel asumirá que el modelo User debe tener un método posts que define la relación. Si es necesario, puedes especificar explícitamente el nombre de la relación que te gustaría manipular:

$user = User::factory()
->has(Post::factory()->count(3), 'posts')
->create();

Por supuesto, puedes realizar manipulaciones de estado en los modelos relacionados. Además, puedes pasar una transformación de estado basada en cierres si tu cambio de estado requiere acceso al modelo padre:

$user = User::factory()
->has(
Post::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
)
->create();

Uso de Métodos Mágicos

Para mayor comodidad, puedes usar los métodos de relación de fábrica mágicos de Laravel para construir relaciones. Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que los modelos relacionados deben crearse mediante un método de relación posts en el modelo User:

$user = User::factory()
->hasPosts(3)
->create();

Cuando uses métodos mágicos para crear relaciones de fábrica, puedes pasar un conjunto de atributos para anular en los modelos relacionados:

$user = User::factory()
->hasPosts(3, [
'published' => false,
])
->create();

Puedes proporcionar una transformación de estado basada en cierres si tu cambio de estado requiere acceso al modelo padre:

$user = User::factory()
->hasPosts(3, function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
->create();

Relaciones Belongs To

Ahora que hemos explorado cómo construir relaciones "has many" utilizando fábricas, exploremos la inversa de la relación. El método for se puede utilizar para definir el modelo principal al que pertenecen los modelos creados por la fábrica. Por ejemplo, podemos crear tres instancias del modelo App\Models\Post que pertenecen a un solo usuario:

use App\Models\Post;
use App\Models\User;
 
$posts = Post::factory()
->count(3)
->for(User::factory()->state([
'name' => 'Jessica Archer',
]))
->create();

Si ya tienes una instancia del modelo principal que debería estar asociada a los modelos que estás creando, puedes pasar la instancia del modelo al método for:

$user = User::factory()->create();
 
$posts = Post::factory()
->count(3)
->for($user)
->create();

Uso de Métodos Mágicos

Para mayor comodidad, puedes usar los métodos de relación de fábrica mágicos de Laravel para definir relaciones "belongs to". Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que las tres publicaciones deben pertenecer a la relación user en el modelo Post:

$posts = Post::factory()
->count(3)
->forUser([
'name' => 'Jessica Archer',
])
->create();

Relaciones Many To Many

Al igual que con relaciones "has many", las relaciones "many to many" se pueden crear utilizando el método has:

use App\Models\Role;
use App\Models\User;
 
$user = User::factory()
->has(Role::factory()->count(3))
->create();

Atributos de Tabla Pivote

Si necesitas definir atributos que se deben establecer en la tabla pivote / intermedia que vincula los modelos, puedes usar el método hasAttached. Este método acepta un array de nombres y valores de atributos de la tabla pivote como su segundo argumento:

use App\Models\Role;
use App\Models\User;
 
$user = User::factory()
->hasAttached(
Role::factory()->count(3),
['active' => true]
)
->create();

Puedes proporcionar una transformación de estado basada en cierres si tu cambio de estado requiere acceso al modelo relacionado:

$user = User::factory()
->hasAttached(
Role::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['name' => $user->name.' Role'];
}),
['active' => true]
)
->create();

Si ya tienes instancias de modelos que te gustaría adjuntar a los modelos que estás creando, puedes pasar las instancias de los modelos al método hasAttached. En este ejemplo, los mismos tres roles se adjuntarán a los tres usuarios:

$roles = Role::factory()->count(3)->create();
 
$user = User::factory()
->count(3)
->hasAttached($roles, ['active' => true])
->create();

Uso de Métodos Mágicos

Para mayor comodidad, puedes usar los métodos de relación de fábrica mágicos de Laravel para definir relaciones "many to many". Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que los modelos relacionados deben crearse mediante un método de relación roles en el modelo User:

$user = User::factory()
->hasRoles(1, [
'name' => 'Editor'
])
->create();

Relaciones Polimórficas

Las relaciones polimórficas también se pueden crear mediante fábricas. Las relaciones polimórficas "morph many" se crean de la misma manera que las relaciones "has many" típicas. Por ejemplo, si un modelo App\Models\Post tiene una relación morphMany con un modelo App\Models\Comment:

use App\Models\Post;
 
$post = Post::factory()->hasComments(3)->create();

Relaciones Morph To

Los métodos mágicos no se pueden utilizar para crear relaciones morphTo. En su lugar, el método for debe utilizarse directamente y el nombre de la relación debe proporcionarse explícitamente. Por ejemplo, imagina que el modelo Comment tiene un método commentable que define una relación morphTo. En esta situación, podemos crear tres comentarios que pertenecen a una sola publicación mediante el uso directo del método for:

$comments = Comment::factory()->count(3)->for(
Post::factory(), 'commentable'
)->create();

Relaciones Polimórficas Many To Many

Las relaciones polimórficas "many to many" (morphToMany / morphedByMany) se pueden crear de la misma manera que las relaciones "many to many" no polimórficas:

use App\Models\Tag;
use App\Models\Video;
 
$videos = Video::factory()
->hasAttached(
Tag::factory()->count(3),
['public' => true]
)
->create();

Por supuesto, también se puede utilizar el método mágico has para crear relaciones polimórficas "many to many":

$videos = Video::factory()
->hasTags(3, ['public' => true])
->create();

Definir Relaciones Dentro de las Fábricas

Para definir una relación dentro de tu fábrica de modelos, normalmente asignarás una nueva instancia de fábrica a la clave foránea de la relación. Esto se hace normalmente para las relaciones "inversas" como las relaciones belongsTo y morphTo. Por ejemplo, si quieres crear un nuevo usuario al crear una publicación, puedes hacer lo siguiente:

use App\Models\User;
 
/**
* Define el estado predeterminado del modelo.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'title' => fake()->title(),
'content' => fake()->paragraph(),
];
}

Si las columnas de la relación dependen de la fábrica que la define, puedes asignar un cierre a un atributo. El cierre recibirá el conjunto de atributos evaluado de la fábrica:

/**
* Define el estado predeterminado del modelo.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'user_type' => function (array $attributes) {
return User::find($attributes['user_id'])->type;
},
'title' => fake()->title(),
'content' => fake()->paragraph(),
];
}

Reciclar un Modelo Existente para Relaciones

Si tienes modelos que comparten una relación común con otro modelo, puedes usar el método recycle para asegurar que una única instancia del modelo relacionado se recicle para todas las relaciones creadas por la fábrica.

Por ejemplo, imagina que tienes los modelos Airline, Flight y Ticket, donde el ticket pertenece a una aerolínea y un vuelo, y el vuelo también pertenece a una aerolínea. Al crear tickets, probablemente querrás la misma aerolínea tanto para el ticket como para el vuelo, así que puedes pasar una instancia de aerolínea al método recycle:

Ticket::factory()
->recycle(Airline::factory()->create())
->create();

Puedes encontrar el método recycle especialmente útil si tienes modelos que pertenecen a un usuario o equipo común.

El método recycle también acepta una colección de modelos existentes. Cuando se proporciona una colección al método recycle, se elegirá un modelo aleatorio de la colección cuando la fábrica necesite un modelo de ese tipo:

Ticket::factory()
->recycle($airlines)
->create();