Как я проектирую миграции и базу в Laravel, чтобы потом не переписывать всё

В Laravel очень легко начать «на коленке»: накидать пару таблиц, связи додумать потом, типы полей взять «на глаз». Через год такой проект обычно просит «срочно всё переделать». Я стараюсь заложить нормальную схему ещё в момент первых миграций.

С чего начинаю проектирование

Перед первой миграцией отвечаю себе на три вопроса:

  • какие основные сущности есть в системе (User, Product, Order и т.д.)
  • какие между ними связи (один-ко-многим, многие-ко-многим)
  • какие сценарии будут самыми частыми (по чему ищем, фильтруем, сортируем)

Из этих ответов рождаются первые таблицы и индексы, а не наоборот.

Базовый шаблон миграции

Я держусь простого и понятного набора полей:

Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('sku')->unique(); $table->decimal('price', 10, 2)->index(); $table->unsignedBigInteger('brand_id')->index(); $table->timestamps(); });

Правила:

  • числовые поля для денег только decimal, никакого float
  • всё, по чему будет поиск/фильтр, сразу получает index()
  • timestamps() ставлю почти везде

Связи: foreign keys или нет Если проект под контролем и миграции проходят через деплой:

  • ставлю foreignId()->constrained() везде, где это логично
  • это помогает поймать мусорные данные ещё на уровне БД

Если база огромная и обновления сложные, могу:

  • оставить unsignedBigInteger + индексы
  • контролировать целостность в приложении

Главное — не мешать подходы и не делать часть таблиц «строгими», а часть «как получится».

Многие-ко-многим и pivot-таблицы Типичный пример: товары и категории.

Schema::create('category_product', function (Blueprint $table) { $table->unsignedBigInteger('category_id'); $table->unsignedBigInteger('product_id');

$table->primary(['category_id', 'product_id']); });

Сразу задаю составной primary key, чтобы не плодить дубликаты. Если у связи есть свои поля (например, quantity или sort), добавляю их сюда же.

Эволюция схемы: отдельные миграции для изменений Самая частая ошибка — править существующие миграции. Я так не делаю:

  • любая правка структуры — новая миграция add_field_to_table, rename_column
  • старые миграции остаются как исторический слепок

Так можно безопасно откатывать и раскатывать изменения на разных окружениях.

Индексы: что я добавляю сразу Я всегда думаю глазами запросов. Индексы ставлю:

  • на поля, участвующие в WHERE и ORDER BY
  • на внешние ключи
  • на сочетания полей, по которым фильтруем вместе

Пример:

$table->index(['status', 'created_at']);

Потом это экономит часы поиска «почему запрос на список заказов так медленный».

Мин**и-чек-лист перед тем, как нажать migrate

*** Понимаю сущности и связи в домене * Денежные поля сделаны через decimal с нужной точностью * Часто используемые поля и связи проиндексированы * Для многих-ко-многим есть pivot-таблицы без дубликатов * Любые изменения схемы идут отдельными миграциями, не правлю старые * Есть план, как откатывать миграции на тесте и проде

Итог Хорошие миграции в Laravel — это не про красивые классы, а про дисциплину: сначала продумать сущности и связи, сразу заложить индексы и аккуратно эволюционировать схему через новые миграции. Тогда даже через пару лет к базе можно спокойно прикасаться, а не бояться, что любое изменение уронит половину проекта.

#laravel #php #database

Как я проектирую миграции и базу в Laravel, чтобы потом не переписывать всё
В Laravel очень легко начать «на коленке»: накидать пару таблиц, связи додумать потом, типы полей взять «на глаз» | Сетка — социальная сеть от hh.ru