Eloquent: Factorias
- Introducción
- Definición de factorias modelo
- Creación de modelos mediante factorias
-
Relaciones de Factoría
- Relaciones "Tiene Muchos" - Has Many Relationships
- Relaciones "Pertenece a" - Belongs To Relationships
- Relaciones "de muchos a muchos" - Many To Many Relationships
- Relaciones polimórficas - Polymorphic Relationships
- Definición de relaciones dentro de las factorias
- Reciclaje de un Modelo Existente de Relaciones
Introducción
Cuando esté probando su aplicación o sembrando su base de datos, puede que necesite insertar algunos registros en su base de datos. En lugar de especificar manualmente el valor de cada columna, Laravel te permite definir un conjunto de atributos por defecto para cada uno de tus modelos Eloquent utilizando factorias de modelos.
Para ver un ejemplo de cómo escribir una factoría, echa un vistazo al fichero database/factories/UserFactory.php
de tu aplicación. Esta factoría se incluye con todas las nuevas aplicaciones Laravel y contiene la siguiente definición de factoría:
namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory;use Illuminate\Support\Str; class UserFactory extends Factory{ /** * Define the model's default state. * * @return array */ public function definition() { 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 factorías son clases que extienden la clase base Factory
de Laravel y definen un método definition
. El método definition
devuelve el conjunto predeterminado de valores de atributo que deben aplicarse al crear un modelo utilizando la factoría.
A través del ayudante fake
, las factorias tienen acceso a la librería Faker PHP, que te permite generar varios tipos de datos aleatorios para pruebas y siembra.
Nota
Puedes establecer el idioma que Faker usará en tu aplicación añadiendo una opciónfaker_locale
a tu fichero de configuraciónconfig/app.php
.
Definición de factorias de modelos
Generación de factorias
Para crear una factoría, ejecuta el comando make:factory
Artisan:
php artisan make:factory PostFactory
La nueva clase de factoría se colocará en su directorio database/factories
.
Convenciones de descubrimiento de modelos y factorias
Una vez que haya definido sus factorias, puede utilizar el método estático factory
proporcionado a sus modelos por el trait Illuminate\Database\Eloquent\Factories\HasFactory
con el fin de crear una instancia de factoría para ese modelo.
El método de factory
del trait HasFactory
utilizará convenciones para determinar la factoría adecuada para el modelo en el que se está usando. En concreto, el método buscará una factoría en el namespace Database\Factories
que tenga un nombre de clase que coincida con el nombre del modelo y que tenga el sufijo Factory
. Si estas convenciones no se aplican a su aplicación o factoría en particular, puede sobrescribir el método newFactory
en su modelo para devolver directamente una instancia de la factoría correspondiente del modelo:
use Database\Factories\Administration\FlightFactory; /** * Create a new factory instance for the model. * * @return \Illuminate\Database\Eloquent\Factories\Factory */protected static function newFactory(){ return FlightFactory::new();}
A continuación, defina una propiedad $model
en la factoría correspondiente:
use App\Administration\Flight;use Illuminate\Database\Eloquent\Factories\Factory; class FlightFactory extends Factory{ /** * The name of the factory's corresponding model. * * @var string */ protected $model = Flight::class;}
Estados de Factoría
Los métodos de manipulación de estado le permiten definir modificaciones que se pueden aplicar a sus factorias de modelos. Por ejemplo, su factoría Database\Factories\UserFactory
podría contener un método de estado suspended
que modifique uno de sus valores de atributo por defecto.
Los métodos de transformación de estado suelen llamar al método state
proporcionado por la clase factoría base de Laravel. El método state
acepta un closure que recibirá el array de atributos raw definidos para la factoría y debería devolver un array de atributos a modificar:
/** * Indicate that the user is suspended. * * @return \Illuminate\Database\Eloquent\Factories\Factory */public function suspended(){ return $this->state(function (array $attributes) { return [ 'account_status' => 'suspended', ]; });}
Estado "Trashed"
Si tu modelo Eloquent puede ser borrado mediante "soft delete", puedes invocar el método de estado "trashed"
incorporado para indicar que el modelo creado ya debe estar marcado como "soft deleted". No es necesario definir manualmente el estado "trashed"
, ya que está disponible automáticamente para todas las factorias:
use App\Models\User; $user = User::factory()->trashed()->create();
Callbacks de Factorías
Los callbacks de factorías se registran usando los métodos afterMaking
y afterCreating
y permiten realizar tareas adicionales después de crear un modelo. Debes registrar estos callbacks definiendo un método configure
en tu clase factoría. Este método será llamado automáticamente por Laravel cuando la factoría sea instanciada:
namespace Database\Factories; use App\Models\User;use Illuminate\Database\Eloquent\Factories\Factory;use Illuminate\Support\Str; class UserFactory extends Factory{ /** * Configure the model factory. * * @return $this */ public function configure() { return $this->afterMaking(function (User $user) { // })->afterCreating(function (User $user) { // }); } // ...}
Creación de modelos mediante factorias
Instanciación de modelos
Una vez que haya definido sus factorias, puede utilizar el método estático factory
proporcionado a sus modelos por el trait Illuminate\Database\Eloquent\Factories\HasFactory
con el fin de crear una instancia de factoría para ese modelo. Veamos algunos ejemplos de creación de modelos. En primer lugar, vamos a utilizar el método make
para crear modelos sin guardarlos 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();
Aplicando Estados
También puedes aplicar cualquiera de tus estados a los modelos. Si desea aplicar múltiples transformaciones de estado a los modelos, puede simplemente llamar directamente a los métodos de transformación de estado:
$users = User::factory()->count(5)->suspended()->make();
Sobreescribiendo Atributos
Si quieres sobreescribir algunos de los valores por defecto de tus modelos, puedes pasar un array de valores al método make
. Sólo los atributos especificados serán reemplazados mientras que el resto de los atributos permanecerán con sus valores por defecto especificados por la factoría:
$user = User::factory()->make([ 'name' => 'Abigail Otwell',]);
De manera alternativa, el método state
puede ser llamado directamente en la instancia de la factoría 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 utilizando factorias.
Persistencia de modelos
El método create
crea instancias del modelo y las guarda en la base de datos utilizando el método save
de Eloquent:
use App\Models\User; // Create a single App\Models\User instance...$user = User::factory()->create(); // Create three App\Models\User instances...$users = User::factory()->count(3)->create();
Puedes sobreescribir los atributos de modelo por defecto de la factoría pasando un array de atributos al método create
:
$user = User::factory()->create([ 'name' => 'Abigail',]);
Secuencias
A veces puede que desee alternar el valor de un atributo de modelo dado para cada modelo creado. Esto se consigue definiendo una transformación de estado como una secuencia. Por ejemplo, puede que desee 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, cinco usuarios serán creados con un valor admin
de Y
y cinco usuarios serán creados con un valor admin
de N
.
Si es necesario, puede incluir un closure como valor de secuencia. El closure se invocará cada vez que la secuencia necesite un nuevo valor:
$users = User::factory() ->count(10) ->state(new Sequence( fn ($sequence) => ['role' => UserRoles::all()->random()], )) ->create();
Dentro del closure, puede acceder a las propiedades $index
o $count
de la instancia de secuencia donde este es inyectado. La propiedad $index
contiene el número de iteraciones a través de la secuencia que se han producido hasta el momento, mientras que la propiedad $count
contiene el número total de veces que la secuencia será invocada:
$users = User::factory() ->count(10) ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) ->create();
Relaciones de Factoría
Relaciones "Tiene Muchos" - Has Many Relationships
A continuación, vamos a explorar la construcción de relaciones entre modelos Eloquent utilizando los métodos fluidos de las factorías de Laravel. En primer lugar, vamos a suponer 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 posts utilizando el método has
proporcionado por las factorías de Laravel. El método has
acepta una instancia de factoría:
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 defina la relación. Si es necesario, puede especificar explícitamente el nombre de la relación que desea 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 un closure 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
Por conveniencia, puedes usar los métodos mágicos de relación de Laravel para construir relaciones. Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que los modelos relacionados deben ser creados a través de un método de relación posts
en el modelo User
:
$user = User::factory() ->hasPosts(3) ->create();
Cuando se utilizan métodos mágicos para crear relaciones de factoría, puede pasar una array de atributos que seran sobreescritos en los modelos relacionados:
$user = User::factory() ->hasPosts(3, [ 'published' => false, ]) ->create();
Puedes proporcionar una transformación de estado basada en un closure 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 "Pertenece a" - Belongs To Relationships
Ahora que hemos explorado cómo construir relaciones "has many" usando factorias, exploremos la inversa de la relación. El método for
puede usarse para definir el modelo padre al que pertenecen los modelos creados por la factoría. Por ejemplo, podemos crear tres instancias del modelo App\Models\Post
que pertenecen a un único usuario:
use App\Models\Post;use App\Models\User; $posts = Post::factory() ->count(3) ->for(User::factory()->state([ 'name' => 'Jessica Archer', ])) ->create();
Si ya tiene una instancia del modelo padre que debería estar asociada con los modelos que está creando, puede 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 utilizar los métodos mágicos de relación de factoría de Laravel para definir las relaciones "belongs to". Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que los tres posts deben pertenecer a la relación user
en el modelo Post
:
$posts = Post::factory() ->count(3) ->forUser([ 'name' => 'Jessica Archer', ]) ->create();
Relaciones "de muchos a muchos" - Many To Many Relationships
Al igual que las relaciones has many, las relaciones "many to many" pueden crearse utilizando el método has
:
use App\Models\Role;use App\Models\User; $user = User::factory() ->has(Role::factory()->count(3)) ->create();
Atributos de la tabla dinámica
Si necesita definir los atributos que deben establecerse en la tabla pivotante / intermedia que vincula los modelos, puede utilizar el método hasAttached
. Este método acepta una array de nombres y valores de atributos de tabla dinámica como segundo argumento:
use App\Models\Role;use App\Models\User; $user = User::factory() ->hasAttached( Role::factory()->count(3), ['active' => true] ) ->create();
Puede proporcionar una transformación de estado basada en el closure si su cambio de estado requiere el 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 dispone de instancias de modelo que desea adjuntar a los modelos que está creando, puede pasar las instancias de modelo 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
Por conveniencia, puedes usar los métodos de relación de la factoría mágica de Laravel para definir relaciones de muchos a muchos. Por ejemplo, el siguiente ejemplo utilizará la convención para determinar que los modelos relacionados deben ser creados a través de un método de relación roles
en el modelo User
:
$user = User::factory() ->hasRoles(1, [ 'name' => 'Editor' ]) ->create();
Relaciones polimórficas - Polymorphic Relationships
Las relaciones polimórficas también pueden crearse utilizando factorias. Las relaciones polimórficas "morph many" se crean de la misma manera que las relaciones típicas "has many". 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();
Morph To Relationships
No se pueden utilizar métodos mágicos para crear relaciones morphTo
. En su lugar, se debe utilizar directamente el método for
y proporcionar explícitamente el nombre de la relación. Por ejemplo, imagine que el modelo Comment
tiene un método commentable
que define una relación morphTo
. En esta situación, podemos crear tres comentarios que pertenezcan a una única entrada utilizando directamente el método for
:
$comments = Comment::factory()->count(3)->for( Post::factory(), 'commentable')->create();
Relaciones polimórficas Many To Many
Las relaciones polimórficas "muchos a muchos"(morphToMany
/ morphedByMany
) pueden crearse igual que las relaciones no polimórficas "muchos a muchos":
use App\Models\Tag;use App\Models\Video; $videos = Video::factory() ->hasAttached( Tag::factory()->count(3), ['public' => true] ) ->create();
Por supuesto, el método mágico has
también puede utilizarse para crear relaciones polimórficas "muchos a muchos":
$videos = Video::factory() ->hasTags(3, ['public' => true]) ->create();
Definición de relaciones dentro de las factorias
Para definir una relación dentro de su factoría de modelos, normalmente asignará una nueva instancia de factoría a la clave externa de la relación. Esto se hace normalmente para las relaciones "inversas" como las relaciones belongsTo
y morphTo
. Por ejemplo, si desea crear un nuevo usuario al crear una entrada, puede hacer lo siguiente:
use App\Models\User; /** * Define the model's default state. * * @return array */public function definition(){ return [ 'user_id' => User::factory(), 'title' => fake()->title(), 'content' => fake()->paragraph(), ];}
Si las columnas de la relación dependen de la factoría que la define puedes asignar un closure a un atributo. El closure recibirá el array atributos evaluados de la factoría:
/** * Define the model's default state. * * @return array */public function definition(){ return [ 'user_id' => User::factory(), 'user_type' => function (array $attributes) { return User::find($attributes['user_id'])->type; }, 'title' => fake()->title(), 'content' => fake()->paragraph(), ];}
Reciclaje de un modelo existente de relaciones
Si tienes modelos que comparten una relación común con otro modelo, puedes utilizar el método recycle
para asegurarte de que una única instancia del modelo relacionado se recicla para todas las relaciones creadas por la factoría.
Por ejemplo, imagine que tiene modelos Aerolínea
, Vuelo
y Billete
, donde el billete pertenece a una aerolínea y a un vuelo, y el vuelo también pertenece a una aerolínea. Al crear billetes, probablemente querrá la misma aerolínea tanto para el billete como para el vuelo, por lo que puede pasar una instancia de aerolínea al método recycle
:
Ticket::factory() ->recycle(Airline::factory()->create()) ->create();
El método recycle
puede resultarle especialmente útil si tiene 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 factoría necesite un modelo de ese tipo:
Ticket::factory() ->recycle($airlines) ->create();