Laravel: создание фабрик и seeders при связях между моделями

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В ситуациях, когда одна модель обязательно должна быть связана с другой моделью (например, статья и ее автор, компания и сотрудники и т.п.), большинство программистов допускают различные ошибки при создании фабрик (Factory) и сидов (Seeders) к этим моделям.

Предлагаю исследовать эту проблему и вывести единственно правильную реализацию создания фабрик и сидов в такой ситуации.

Спойлер: фабрики не должны зависеть от сидов.

Подготовка проекта

Возьмем очень простой пример проекта. На сайте есть список статей, где отображается название статьи и ее автор. Авторы — это пользователи сайта, каждая статья обязательно должна иметь автора. При этом мы хотим сделать так, чтобы при выполнении сидеров создавался только один пользователь и все статьи были привязаны к нему.

Создание и настройка проекта

Подготовим этот проект (готовую реализацию уже можно посмотреть в этом репозитории). Создаем модель, миграцию, фабрику и сид для статей. Таблица с пользователями достаточна той версии, что идет в Laravel из коробки.

Создадим новую установка Laravel, используя установщик, и ставим проект с помощью консольной команды.

laravel new relation_factories

Теперь настроим файл .env, чтобы не создавать отдельную БД, для ускорения можно использовать БД sqlite.

// Параметры файла .env

DB_CONNECTION=sqlite
DB_DATABASE=/path...to...project/database/database.sqlite

Создать файл с БД можно также простой консольной командой.

touch database/database.sqlite

Создание модели статьи

Пока мы находимся недалеко от консоли, создадим модель с миграцией и фабрикой, используя консольную команду.

php artisan make:model -f -m Article

На следующем шаге необходимо описать миграцию статьи. Здесь все стандартно — добавим поле для хранения заголовка и обязательную ссылку на пользователя.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateArticlesTable extends Migration
{
   public function up()
   {
       Schema::create('articles', function (Blueprint $table) {
           $table->id();
           $table->string('title');
           $table->foreignId('author_id')
               ->references('id')
               ->on('users')
               ->cascadeOnDelete()
           ;
           $table->timestamps();
       });
   }

   public function down()
   {
       Schema::dropIfExists('articles');
   }
}

После создания миграции, добавим описание метода для связи с автором в классе статьи. 

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Article extends Model
{
   use HasFactory;

   public function author(): BelongsTo
   {
       return $this->belongsTo(User::class, 'author_id');
   }
}

В завершении первой части подготовки остается только выполнить миграцию. Используем знакомую консольную команду.

php artisan migrate

Создание фабрики и сидов

Базовая часть подготовки проекта готова, мы можем перейти к описанию фабрики и сида.

Начнем с фабрики. У статьи есть только одно поле с данным — это заголовок, заполним его случайным предложением.

<?php

namespace Database\Factories;

use App\Models\Article;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class ArticleFactory extends Factory
{
   protected $model = Article::class;

   public function definition(): array
   {
       return [
           'title' => $this->faker->sentence,
       ];
   }
}

Опытный читатель уже заметил здесь ошибку, но мы вернемся к ней чуть позже, а пока опишем код сида. Будем писать код сразу в классе DatabaseSeeder, для упрощения без создания отдельных сидов.

Создадим, как того требует задание, одного пользователя и пять статей.

<?php

namespace Database\Seeders;

use App\Models\Article;
use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{

   public function run()
   {
        User::factory()->create();
        Article::factory()->count(5)->create();
   }
}

А теперь попробуем выполнить сидер с помощью консольной команды.

php artisan db:seed

При попытке ее выполнить мы получили фатальную ошибку.

Illuminate\Database\QueryException 

SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: articles.author_id ...

Как создать фабрику неправильно, вариант №1

Мы не можем создать статью, не описав обязательное поле для связи с автором. Исправим это.

Первая мысль, которая, наверняка, приходит в голову. Поскольку внутри сида мы сначала создаем пользователя, а потом статьи, значит в фабрике мы можем выбрать нужного пользователя и привязать его к статье.

Это можно сделать разными неправильными

Источник: https://habr.com/ru/post/645055/


Интересные статьи

Интересные статьи

Компания «Деловой разговор» — Титановый партнер 3СХ — осуществила расширенную интеграцию IP-АТС 3CX с Битрикс 24. Ранее уже существовали отдельные модули, решающие конкретные задачи, напр...
Я давно знаком с Битрикс24, ещё дольше с 1С-Битрикс и, конечно же, неоднократно имел дела с интернет-магазинами которые работают на нём. Да, конечно это дорого, долго, местами неуклюже...
В этом посте я покажу, как использовал автоматы падающего песка для генерации анимаций смерти монстров в моей игре Vagabond. Автоматы падающего песка Автомат падающего песка — это...
Привет, Хабр! Предлагаю вашему вниманию перевод статьи основателя сервиса Meetspaceapp Nick Gauthier «Building Minimal Docker Containers for Go Applications». Время чтения: 6 минут Существу...
Создание первой цепочки DevOps за пять шагов для новичков. DevOps стал панацеей для слишком медленных, разобщенных и прочих проблемных процессов разработки. Но нужны минимальные познания в Dev...