Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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().

Докладніше в документації: Використання фабрик моделей