Якщо під час міграції створювати зовнішні ключі, може виникнути ситуація, що таблицю створено успішно, але зовнішній ключ не вдалося. Тоді ваша міграція буде "наполовину успішною", і якщо ви повторно запустите її після виправлення, з'явиться повідомлення "Таблиця вже існує". Що робити?
Проблема: пояснення
Спочатку дозвольте мені детально пояснити проблему. Ось приклад.
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 DB.
Тепер справжня проблема: якщо виправити помилку в тій же міграції і просто запустити 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` (...)
Отже, чи варто створювати нову міграцію? Відкочування? Дозвольте мені пояснити мій улюблений спосіб вирішення цієї проблеми.
Рішення: Схема::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 і повторно запустити міграцію з виправленням, але ви не завжди маєте доступ до бази даних, якщо вона віддалена. Крім того, не ідеально виконувати будь-які ручні операції з базою даних, якщо ви використовуєте міграцію. Це може бути нормально для вашої локальної бази даних, але це рішення вище було б універсальним для будь-яких локальних/віддалених баз даних.