Миграции: версионирование базы данных
Миграции — это «версии» структуры базы данных в виде PHP-кода: они позволяют создавать и менять таблицы предсказуемо и одинаково на всех машинах команды.
Суть: вместо ручного SQL вы описываете таблицы PHP-кодом в файлах-миграциях. Метод
up()применяет изменения,down()— откатывает. Командаphp artisan migrateпрогоняет их.
Как синхронизировать структуру базы между разработчиками? Если каждый вручную создаёт таблицы через GUI, рано или поздно у всех будут разные схемы, и проект сломается. Миграции решают это: структура БД описывается кодом, который хранится в git вместе с проектом. Любой разработчик запускает одну команду — и получает точно такую же базу.
Миграция — это файл в database/migrations с временной меткой в имени (порядок применения). Внутри два метода: up() описывает, что сделать (создать таблицу, добавить колонку), down() — как откатить. Благодаря этому изменения можно безопасно применять и отменять.
Создание миграции
# создать миграцию для таблицы products
php artisan make:migration create_products_table
# применить все новые миграции
php artisan migrate
# откатить последнюю партию
php artisan migrate:rollback<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id(); // первичный ключ
$table->string('name'); // VARCHAR
$table->text('description')->nullable();
$table->decimal('price', 8, 2); // цена
$table->boolean('is_active')->default(true);
$table->timestamps(); // created_at, updated_at
});
}
public function down(): void
{
Schema::dropIfExists('products');
}
};Как работает под капотом
Laravel хранит специальную таблицу migrations, где отмечает, какие файлы уже применены. Когда вы запускаете migrate, фреймворк смотрит, каких миграций ещё нет в этой таблице, и выполняет их по порядку временных меток, превращая методы Schema в SQL-команды CREATE TABLE. Откат читает ту же таблицу и выполняет down() в обратном порядке.
database/migrations/
+-----------------------------------+
| 2024_01_01_create_users_table | -> applied
| 2024_01_02_create_products_table | -> applied
| 2024_01_03_add_sku_to_products | -> PENDING
+-----------------------------------+
|
php artisan migrate
v
выполняет только PENDING, пишет в таблицу migrations
Смоделируем учёт применённых миграций на Python: применяем только те, которых ещё нет в журнале.
Попробуй сам ▶
# Менеджер миграций: применяет только новые
all_migrations = [
'2024_01_01_create_users',
'2024_01_02_create_products',
'2024_01_03_add_sku',
]
applied = {'2024_01_01_create_users', '2024_01_02_create_products'}
def migrate(all_m, applied):
for m in all_m:
if m in applied:
print('пропуск (уже есть):', m)
else:
print('ПРИМЕНЯЮ :', m)
applied.add(m)
migrate(all_migrations, applied)
print('Итого применено:', len(applied))
Частые ошибки
- Править применённую миграцию. Если миграция уже выполнена у коллег, меняйте схему новой миграцией, а не редактируйте старую.
- Пустой
down(). Без корректного откатаmigrate:rollbackне сработает. migrate:freshна проде. Эта команда удаляет все таблицы — на боевом сервере это потеря данных.
Best practices
- Делайте каждую миграцию атомарной: одно логическое изменение на файл.
- Всегда заполняйте
down(), чтобы откат работал. - Используйте
nullable(),default()и индексы прямо в схеме.
Миграции описывают не только создание таблиц, но и их изменение. Команда php artisan make:migration add_sku_to_products_table создаёт миграцию, где в up() вызывают Schema::table('products', ...) и добавляют колонку, а в down() — удаляют её. Так структура развивается версия за версией, и каждый шаг обратим. Отдельная важная тема — внешние ключи: метод $table->foreignId('user_id')->constrained()->cascadeOnDelete() создаёт колонку, связывает её с таблицей users и настраивает каскадное удаление. Это переносит часть логики целостности на уровень базы, где она надёжнее всего. Полезны и индексы: $table->index('email') ускоряет поиск, а $table->unique('email') гарантирует уникальность на уровне СУБД. Продумывание индексов на этапе миграции избавляет от мучительной оптимизации запросов в будущем, когда таблица разрастётся.
Итог: миграции версионируют структуру БД кодом, синхронизируя её между всеми разработчиками. Дальше познакомимся с Eloquent — слоем, который превращает строки таблиц в удобные PHP-объекты.