Зачем вообще миграции:
Миграция это PHP-файл, который описывает одно изменение схемы: создать таблицу, добавить колонку, повесить индекс. Файлы лежат в database/migrations и попадают в git вместе с остальным кодом. Коллега делает git pull, запускает одну команду и получает ту же структуру базы, что у вас. Никаких "скинь дамп" и SQL-скриптов в чате.
Подключение к базе:
Laravel начиная с 11 версии по умолчанию использует SQLite, файл database/database.sqlite создается при установке проекта. Для учебы этого хватает, но мы сразу настроим MySQL, потому что на боевом хостинге чаще всего будет он или PostgreSQL. Открываем .env:
Код: Выделить всё
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=root
DB_PASSWORD=secretПервая миграция:
Продолжим блог из прошлых глав, ему нужна таблица постов. Основной набор команд выглядит так:
Код: Выделить всё
php artisan make:migration create_posts_table
php artisan migrate
php artisan migrate:statusКод: Выделить всё
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 200);
$table->string('slug')->unique();
$table->text('body');
$table->boolean('is_published')->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};Команда migrate выполняет все миграции, которые еще не применялись, и записывает их в служебную таблицу migrations. Состояние смотрим через migrate:status. Откатить последнюю пачку: migrate:rollback. Снести все таблицы и накатить с нуля: migrate:fresh. На локалке это нормальный рабочий инструмент, на проде путь к увольнению, команда удаляет все таблицы вместе с данными.
Меняем существующую таблицу:
Схема никогда не финальна. Допустим, постам понадобился автор. В старую миграцию не лезем, создаем новую: make:migration add_user_id_to_posts_table с флагом --table=posts. Внутри:
Код: Выделить всё
public function up(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')
->after('id')
->constrained()
->cascadeOnDelete();
});
}
public function down(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->dropConstrainedForeignId('user_id');
});
}Типичные грабли:
Правка уже выполненной миграции. На вашей машине она применилась, повторный migrate ее не тронет, а у коллеги и на проде выполнится уже исправленная версия. Базы разъедутся, и поймать это потом тяжело. Правило: миграция ушла в общую ветку, значит она неприкосновенна, любые изменения только новым файлом. Пока работаете один на локалке, можно откатить через rollback, поправить и накатить снова.
Пустой down(). Заготовка генерирует откат, не выкидывайте его. Без рабочего down() команда rollback однажды упадет в самый неподходящий момент.
Порядок миграций с внешними ключами. Файл с foreignId на users должен выполняться после создания users. Даты в именах обычно это гарантируют, но при склейке веток порядок может перемешаться, проверяйте на ревью.
SQLite локально, MySQL на проде. Работает, пока не наткнетесь на различия в поведении (SQLite заметно терпимее к типам и ограничениям). Надежнее держать локально ту же СУБД, что на боевом сервере, с Docker или Laravel Sail это вопрос пяти минут.
Что усвоили: миграции версионируют схему базы так же, как git версионирует код. Структура описывается через Schema и Blueprint, изменения накатываются вперед новыми файлами, а не правкой старых, и для каждого изменения есть откат. В следующей главе на эти таблицы лягут модели Eloquent, и мы наконец начнем читать и писать данные из контроллеров.