При создании внешних ключей при миграции может возникнуть ситуация, когда таблица будет успешно создана, но внешний ключ завершится сбоем. Тогда ваша миграция будет «наполовину успешной», и если вы повторно запустите ее после исправления, она скажет «Таблица уже существует». Что делать?
Проблема: объяснение
Во-первых, позвольте мне подробно объяснить проблему. Вот пример.
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('team_league_id')->constrained();
$table->timestamps();
});
Код выглядит неплохо, правда? А что, если таблица, на которую указывает «team_leagues», не существует? А может по-другому называется? Тогда вы увидите эту ошибку в терминале:
2023_06_05_143926_create_teams_table ..................................................................... 20ms FAIL
Illuminate\Database\QueryException
SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table 'team_leagues'
(Connection: mysql, SQL: alter table `teams` add constraint `teams_team_league_id_foreign` foreign key (`team_league_id`) references `team_leagues` (`id`))
Но это только часть проблемы. Итак, хорошо, вы поняли, что упомянутая таблица называется «лиги», а не «team_leagues». Возможные варианты исправления:
- Либо переименуйте поле «team_league_id» в просто «league_id»
- Или укажите таблицу
->constrained('leagues')
Но реальная проблема сейчас заключается в состоянии базы данных:
- Таблица
teams
уже создана - Но иностранный ключ к лигам провалился!
Это означает, что в таблице баз данных системы Laravel нет записей об успешном завершении миграции.
Теперь реальная проблема: если вы исправите ошибку в той же миграции и просто запустите php artisan migrate
, он скажет: «Таблица уже существует».
2023_06_05_143926_create_teams_table ...................................................................... 3ms FAIL
Illuminate\Database\QueryException
SQLSTATE[42S01]: Base table or view already exists:
1050 Table 'teams' already exists
(Connection: mysql, SQL: create table `teams` (...)
Так стоит ли создавать новую миграцию? Откат? Позвольте мне объяснить мой любимый способ решения этой проблемы.
Решение: Schema::hasTable() и отдельный внешний ключ
Вы можете повторно запустить миграцию для уже существующих таблиц и убедиться, что они будут созданы только в том случае, если они не существуют с помощью метода Schema::hasTable()
.
Но затем нам нужно разделить их foreignId()
на части, потому что на самом деле это метод 2-в-1: он создает столбец (который преуспел) и внешний ключ (который не удался).
Итак, мы переписываем миграцию в это:
if (! Schema::hasTable('teams')) {
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('team_league_id');
$table->timestamps();
});
}
// This may be in the same migration file or in a separate one
Schema::table('teams', function (Blueprint $table) {
$table->foreign('team_league_id')->constrained('leagues');
});
Теперь, если вы запустите php artisan migrate
, он успешно выполнит полную миграцию.
Конечно, альтернативным решением было бы вручную удалить таблицу teams
через SQL-клиент и повторно запустить миграцию с исправлением, но у вас не всегда есть доступ к базе данных, если она удаленная. Кроме того, не рекомендуется выполнять какие-либо ручные операции с базой данных, если вы используете миграции. Это может быть нормально для вашей локальной базы данных, но это решение, приведенное выше, будет универсальным для любых локальных/удаленных баз данных.