Middle: питання на співбесіді з теми «Сидери»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
3 питання
Їх часто плутають, бо обидва створюють записи. Різниця в тому, що саме вони створюють.
Фабрика описує, як виглядає один правдоподібний запис:
class VacancyFactory extends Factory
{
public function definition(): array
{
return [
'title' => fake()->jobTitle(),
'level' => fake()->randomElement(VacancyLevel::cases()),
'is_published' => true,
];
}
public function draft(): static
{
return $this->state(fn () => ['is_published' => false]);
}
}
Використовується переважно в тестах, де потрібен запис із конкретною властивістю:
$vacancy = Vacancy::factory()->draft()->create();
Сідер заповнює базу набором даних - і зазвичай викликає фабрики:
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call(RolesSeeder::class); // довідник: конкретні значення
Vacancy::factory()->count(50)->create(); // демо-дані: випадкові
}
}
Коли що потрібне:
- Довідники - ролі, категорії, статуси, налаштування. Це реальні дані застосунку, у них немає випадковості, і вони мають бути ідемпотентними:
firstOrCreate()абоupdateOrCreate(), щоб повторний запуск не плодив дублів. - Демо-дані для локальної розробки - фабрики всередині сідера.
- Тести - фабрики напряму, без сідера: кожен тест створює рівно те, що перевіряє.
Головне правило: сідер із довідником має бути безпечним для повторного запуску, бо його виконують і на проді. Сідер з демо-даними на прод не потрапляє взагалі.
Через трейт WithoutModelEvents у сідері.
class UserSeeder extends Seeder
{
use WithoutModelEvents;
public function run(): void
{
User::factory()->count(1000)->create();
}
}
Без нього кожен створений користувач запускає спостерігачів: вітальний лист, запис в аналітику, синхронізацію з CRM. Тисяча тестових користувачів - тисяча листів, а локально ще й довге очікування.
Деталі:
- трейт діє й на сідери, викликані через
$this->call(); DatabaseSeederу свіжому застосунку вже має цей трейт;- для частини коду -
Model::withoutEvents(fn () => ...).
Підводний камінь: якщо на подіях моделі тримається обов'язкова логіка - генерація UUID, slug, денормалізований лічильник, - вона теж не спрацює. Такі значення задають у фабриці явно або виносять з подій у мутатори й значення за замовчуванням.
Для повної ізоляції від зовнішнього світу під час локального сідування допомагає ще й MAIL_MAILER=log у .env.
Сідер описує, скільки й яких даних потрібно, а фабрика - як виглядає один запис.
public function run(): void
{
User::factory()
->count(50)
->has(Post::factory()->count(3)->published())
->create();
User::factory()->admin()->create(['email' => 'admin@example.test']);
}
Що робить дані корисними для розробки:
- стани фабрики (
published(),admin(),suspended()) - видно крайні випадки, а не лише «середній» запис; - зв'язки через
has()іfor()- сторінки показують реальну структуру; Sequence- чергування значень: половина активних, половина ні;- відомий обліковий запис для входу - з фіксованим email і паролем з
.env.example.
Відтворюваність: fake()->seed(1234) на початку робить згенеровані дані однаковими між запусками - зручно, коли багу треба відтворити на тих самих даних.
Швидкість: тисячі записів через create() повільні, бо кожен - окремий INSERT з подіями. Для великих обсягів беруть WithoutModelEvents, транзакцію навколо сідування або insert() пачками з масивів, згенерованих make()->toArray().