Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
При тестировании вашего приложения или заполнении вашей базы данных вам может потребоваться вставить несколько записей в базу данных. Вместо того чтобы вручную указывать значение каждого столбца, Laravel позволяет вам определить набор атрибутов по умолчанию для каждой из ваших моделей Eloquent, используя фабрики моделей.
Чтобы увидеть пример написания фабрики, посмотрите файл database/factories/UserFactory.php
в вашем приложении. Эта фабрика включена во все новые приложения Laravel и содержит следующее определение фабрики:
namespace Database\Factories; use Illuminate\Support\Str;use Illuminate\Database\Eloquent\Factories\Factory; class UserFactory extends Factory{ /** * Определить состояние модели по умолчанию. * * @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), ]; }}
Как видите, в своей наиболее базовой форме фабрики - это классы, которые расширяют базовый класс фабрики Laravel и определяют метод definition
. Метод definition
возвращает набор атрибутов по умолчанию, которые должны быть применены при создании модели с использованием фабрики.
С помощью вспомогательной функции fake
фабрики имеют доступ к библиотеке PHP Faker, которая позволяет удобно генерировать различные виды случайных данных для тестирования и заполнения базы данных.
Примечание Вы можете установить локаль Faker для вашего приложения, добавив опцию
faker_locale
в файл конфигурацииconfig/app.php
.
Чтобы создать фабрику, выполните команду Artisan make:factory
:
php artisan make:factory PostFactory
Новый класс фабрики будет размещен в вашем каталоге database/factories
.
После того как вы определили ваши фабрики, вы можете использовать статический метод factory
, предоставленный вашим моделям с использованием трейта Illuminate\Database\Eloquent\Factories\HasFactory
, чтобы создать экземпляр фабрики для этой модели.
Метод factory
трейта HasFactory
будет использовать соглашения для определения соответствующей фабрики для модели, к которой присоединен трейт. В частности, метод будет искать фабрику в пространстве имен Database\Factories
, у которой имя класса совпадает с именем модели и суффиксировано Factory
. Если эти соглашения не применимы к вашему конкретному приложению или фабрике, вы можете переопределить метод newFactory
в своей модели, чтобы вернуть экземпляр соответствующей фабрики модели напрямую:
use Illuminate\Database\Eloquent\Factories\Factory;use Database\Factories\Administration\FlightFactory; /** * Создать новый экземпляр фабрики для модели. */protected static function newFactory(): Factory{ return FlightFactory::new();}
Затем определите свойство model
в соответствующей фабрике:
use App\Administration\Flight;use Illuminate\Database\Eloquent\Factories\Factory; class FlightFactory extends Factory{ /** * Имя соответствующей модели фабрики. * * @var class-string<\Illuminate\Database\Eloquent\Model> */ protected $model = Flight::class;}
Методы управления состоянием позволяют вам определить дискретные модификации, которые могут быть применены к вашим фабрикам моделей в любой комбинации. Например, ваша фабрика Database\Factories\UserFactory
может содержать метод состояния suspended
, который изменяет одно из ее значений атрибутов по умолчанию.
Методы трансформации состояния обычно вызывают метод state
, предоставленный базовым классом фабрики Laravel. Метод state
принимает замыкание, которое получит массив необработанных атрибутов, определенных для фабрики, и должно вернуть массив атрибутов для модификации:
use Illuminate\Database\Eloquent\Factories\Factory; /** * Указывает, что пользователь приостановлен. */public function suspended(): Factory{ return $this->state(function (array $attributes) { return [ 'account_status' => 'suspended', ]; });}
Если ваша модель Eloquent может быть мягко удалена, вы можете вызвать встроенный метод состояния trashed
, чтобы указать, что созданная модель должна быть уже «мягко удалена». Вам не нужно вручную определять состояние trashed
, так как оно автоматически доступно всем фабрикам:
use App\Models\User; $user = User::factory()->trashed()->create();
Фабричные обратные вызовы регистрируются с использованием методов afterMaking
и afterCreating
и позволяют выполнять дополнительные задачи после создания модели или после создания модели. Вы должны зарегистрировать эти обратные вызовы, определив метод configure
в вашем классе фабрики. Этот метод будет автоматически вызван Laravel при создании экземпляра фабрики:
namespace Database\Factories; use App\Models\User;use Illuminate\Database\Eloquent\Factories\Factory; class UserFactory extends Factory{ /** * Настроить фабрику модели. */ public function configure(): static { return $this->afterMaking(function (User $user) { // ... })->afterCreating(function (User $user) { // ... }); } // ...}
Вы также можете регистрировать обратные вызовы фабрики внутри методов состояний, чтобы выполнять дополнительные задачи, которые специфичны для данного состояния:
use App\Models\User;use Illuminate\Database\Eloquent\Factories\Factory; /** * Указывает, что пользователь приостановлен. */public function suspended(): Factory{ return $this->state(function (array $attributes) { return [ 'account_status' => 'suspended', ]; })->afterMaking(function (User $user) { // ... })->afterCreating(function (User $user) { // ... });}
После того как вы определили свои фабрики, вы можете использовать статический метод factory
, предоставленный вашим моделям трейтом Illuminate\Database\Eloquent\Factories\HasFactory
, чтобы создать экземпляр фабрики для этой модели. Давайте рассмотрим несколько примеров создания моделей. Сначала мы используем метод make
, чтобы создать модели без их сохранения в базе данных:
use App\Models\User; $user = User::factory()->make();
Вы можете создать коллекцию из нескольких моделей, используя метод count
:
$users = User::factory()->count(3)->make();
Вы также можете применить любые из ваших состояний к моделям. Если вы хотите применить несколько трансформаций состояния к моделям, вы можете просто вызвать методы трансформации состояния напрямую:
$users = User::factory()->count(5)->suspended()->make();
Если вы хотите переопределить некоторые значения по умолчанию ваших моделей, вы можете передать массив значений методу make
. Только указанные атрибуты будут заменены, в то время как остальные атрибуты останутся установленными в свои значения по умолчанию, как указано в фабрике:
$user = User::factory()->make([ 'name' => 'Abigail Otwell',]);
В качестве альтернативы метод state
может быть вызван напрямую для выполнения встроенного преобразования состояния:
$user = User::factory()->state([ 'name' => 'Abigail Otwell',])->make();
Примечание Защита от массового присвоения автоматически отключается при создании моделей с использованием фабрик.
Метод create
создает экземпляры моделей и сохраняет их в базу данных, используя метод save
Eloquent:
use App\Models\User; // Создать один экземпляр App\Models\User...$user = User::factory()->create(); // Создать три экземпляра App\Models\User...$users = User::factory()->count(3)->create();
Вы можете переопределить атрибуты модели по умолчанию фабрики, передав массив атрибутов методу create
:
$user = User::factory()->create([ 'name' => 'Abigail',]);
Иногда вам может потребоваться чередовать значение заданного атрибута модели для каждой созданной модели. Вы можете сделать это, определив трансформацию состояния в виде последовательности. Например, вы можете чередовать значение столбца admin
между Y
и N
для каждого созданного пользователя:
use App\Models\User;use Illuminate\Database\Eloquent\Factories\Sequence; $users = User::factory() ->count(10) ->state(new Sequence( ['admin' => 'Y'], ['admin' => 'N'], )) ->create();
В этом примере будет создано пять пользователей со значением admin
равным Y
и пять пользователей со значением admin
равным N
.
При необходимости вы можете включить замыкание в качестве значения последовательности. Замыкание будет вызываться каждый раз, когда последовательности потребуется новое значение:
use Illuminate\Database\Eloquent\Factories\Sequence; $users = User::factory() ->count(10) ->state(new Sequence( fn (Sequence $sequence) => ['role' => UserRoles::all()->random()], )) ->create();
Внутри замыкания последовательности вы можете получить доступ к свойствам $index
или $count
экземпляра последовательности, внедренного в замыкание. Свойство $index
содержит количество итераций через последовательность до сих пор, в то время как свойство $count
содержит общее количество вызовов последовательности:
$users = User::factory() ->count(10) ->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index]) ->create();
Для удобства последовательности также могут быть применены с использованием метода sequence
, который просто внутренне вызывает метод state
. Метод sequence
принимает замыкание или массивы атрибутов последовательности:
$users = User::factory() ->count(2) ->sequence( ['name' => 'First User'], ['name' => 'Second User'], ) ->create();
Теперь давайте рассмотрим создание отношений моделей Eloquent с использованием методов Laravel для создания фабрик. Допустим, у нашего приложения есть модель App\Models\User
и модель App\Models\Post
. Также предположим, что модель User
определяет отношение hasMany
с Post
. Мы можем создать пользователя с тремя сообщениями, используя метод has
, предоставленный фабриками Laravel. Метод has
принимает экземпляр фабрики:
use App\Models\Post;use App\Models\User; $user = User::factory() ->has(Post::factory()->count(3)) ->create();
По соглашению, передача модели Post
в метод has
позволяет Laravel предположить, что модель User
должна иметь метод posts
, который определяет отношение. При необходимости вы можете явно указать имя отношения, которое вы хотите изменить:
$user = User::factory() ->has(Post::factory()->count(3), 'posts') ->create();
Конечно же, вы можете выполнять трансформации состояния для связанных моделей. Кроме того, вы можете передать трансформацию состояния на основе замыкания, если изменение состояния требует доступа к родительской модели:
$user = User::factory() ->has( Post::factory() ->count(3) ->state(function (array $attributes, User $user) { return ['user_type' => $user->type]; }) ) ->create();
Для удобства вы можете использовать волшебные методы отношений фабрик Laravel для создания отношений. Например, в следующем примере будет использоваться соглашение о том, что связанные модели должны быть созданы через метод отношения posts
модели User
:
$user = User::factory() ->hasPosts(3) ->create();
При использовании волшебных методов для создания отношений фабрик вы можете передать массив атрибутов для переопределения в связанных моделях:
$user = User::factory() ->hasPosts(3, [ 'published' => false, ]) ->create();
Вы можете предоставить трансформацию состояния на основе замыкания, если изменение состояния требует доступа к родительской модели:
$user = User::factory() ->hasPosts(3, function (array $attributes, User $user) { return ['user_type' => $user->type]; }) ->create();
Теперь, когда мы рассмотрели, как создавать отношения "один ко многим" с использованием фабрик, давайте рассмотрим обратное отношение. Метод for
может быть использован для определения родительской модели, к которой принадлежат созданные фабрикой модели. Например, мы можем создать три экземпляра модели App\Models\Post
, принадлежащих одному пользователю:
use App\Models\Post;use App\Models\User; $posts = Post::factory() ->count(3) ->for(User::factory()->state([ 'name' => 'Jessica Archer', ])) ->create();
Если у вас уже есть экземпляр родительской модели, который должен быть связан с создаваемыми моделями, вы можете передать экземпляр модели методу for
:
$user = User::factory()->create(); $posts = Post::factory() ->count(3) ->for($user) ->create();
Для удобства вы можете использовать волшебные методы отношений фабрик Laravel для определения отношений "принадлежит". Например, в следующем примере будет использоваться соглашение о том, что три сообщения должны принадлежать отношению user
модели Post
:
$posts = Post::factory() ->count(3) ->forUser([ 'name' => 'Jessica Archer', ]) ->create();
Как и отношения "один ко многим", отношения "многие ко многим" могут быть созданы с использованием метода has
:
use App\Models\Role;use App\Models\User; $user = User::factory() ->has(Role::factory()->count(3)) ->create();
Если вам нужно определить атрибуты, которые должны быть установлены в связующей таблице / промежуточной таблице, соединяющей модели, вы можете использовать метод hasAttached
. Этот метод принимает массив имен атрибутов и их значений вторым аргументом:
use App\Models\Role;use App\Models\User; $user = User::factory() ->hasAttached( Role::factory()->count(3), ['active' => true] ) ->create();
Вы можете предоставить трансформацию состояния на основе замыкания, если ваше изменение состояния требует доступа к связанной модели:
$user = User::factory() ->hasAttached( Role::factory() ->count(3) ->state(function (array $attributes, User $user) { return ['name' => $user->name.' Role']; }), ['active' => true] ) ->create();
Если у вас уже есть экземпляры моделей, которые вы хотели бы присоединить к создаваемым моделям, вы можете передать экземпляры моделей методу hasAttached
. В этом примере три роли будут присоединены ко всем трем пользователям:
$roles = Role::factory()->count(3)->create(); $user = User::factory() ->count(3) ->hasAttached($roles, ['active' => true]) ->create();
Для удобства вы можете использовать волшебные методы отношений фабрик Laravel для определения отношений "многие ко многим". Например, в следующем примере будет использоваться соглашение о том, что связанные модели должны быть созданы через метод отношения roles
модели User
:
$user = User::factory() ->hasRoles(1, [ 'name' => 'Editor' ]) ->create();
Полиморфные отношения также могут быть созданы с использованием фабрик. Полиморфные отношения "морф многое" создаются так же, как и типичные отношения "многое ко многим". Например, если у модели App\Models\Post
есть отношение morphMany
с моделью App\Models\Comment
:
use App\Models\Post; $post = Post::factory()->hasComments(3)->create();
Волшебные методы не могут использоваться для создания отношений morphTo
. Вместо этого следует использовать метод for
напрямую, и явно указать имя отношения. Например, представьте, что у модели Comment
есть метод commentable
, который определяет отношение morphTo
. В этой ситуации мы можем создать три комментария, принадлежащих одному посту, используя метод for
напрямую:
$comments = Comment::factory()->count(3)->for( Post::factory(), 'commentable')->create();
Полиморфные отношения "многие ко многим" (morphToMany
/ morphedByMany
) можно создавать так же, как и не полиморфные отношения "многие ко многим":
use App\Models\Tag;use App\Models\Video; $videos = Video::factory() ->hasAttached( Tag::factory()->count(3), ['public' => true] ) ->create();
Конечно же, магический метод has
также может использоваться для создания полиморфных отношений "многие ко многим":
$videos = Video::factory() ->hasTags(3, ['public' => true]) ->create();
Чтобы определить отношение внутри фабрики модели, обычно вы присваиваете новый экземпляр фабрики внешнему ключу отношения. Это обычно делается для "инвертированных" отношений, таких как отношения belongsTo
и morphTo
. Например, если вы хотите создать нового пользователя при создании сообщения, вы можете сделать следующее:
use App\Models\User; /** * Определить состояние модели по умолчанию. * * @return array<string, mixed> */public function definition(): array{ return [ 'user_id' => User::factory(), 'title' => fake()->title(), 'content' => fake()->paragraph(), ];}
Если столбцы отношения зависят от фабрики, которая его определяет, вы можете присвоить замыкание атрибуту. Замыкание получит массив вычисленных атрибутов фабрики:
/** * Определить состояние модели по умолчанию. * * @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(), ];}
Если у вас есть модели, которые имеют общее отношение с другой моделью, вы можете использовать метод recycle
, чтобы обеспечить повторное использование одного экземпляра связанной модели для всех создаваемых отношений фабрик.
Например, представьте, что у вас есть модели Airline
, Flight
и Ticket
, где билет принадлежит авиакомпании и рейсу, и рейс также принадлежит авиакомпании. При создании билетов вы, вероятно, захотите, чтобы у обоих билетов и рейса была одна и та же авиакомпания, поэтому вы можете передать экземпляр авиакомпании методу recycle
:
Ticket::factory() ->recycle(Airline::factory()->create()) ->create();
Метод recycle
может оказаться особенно полезным, если у вас есть модели, принадлежащие общему пользователю или команде.
Метод recycle
также принимает коллекцию существующих моделей. Когда коллекция предоставляется методу recycle
, случайная модель из коллекции будет выбрана, когда фабрика понадобится модель этого типа:
Ticket::factory() ->recycle($airlines) ->create();