Documentación de Laravel 10.x
Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.
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ónconfig/app.php
.
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
.
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;}
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', ]; });}
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();
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) { // ... });}
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();
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();
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.
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',]);
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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(), ];}
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();