Jeśli podczas migracji utworzysz klucze obce, może wystąpić sytuacja, że tabela zostanie utworzona pomyślnie, ale klucz obcy nie powiedzie się. Następnie migracja jest "w połowie udana", a jeśli ponownie uruchomisz ją po naprawie, powie "Tabela już istnieje". Co robić?
Problem: Wyjaśnione
Najpierw pozwól mi szczegółowo wyjaśnić problem. Oto przykład.
Schema::create('teams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('team_league_id')->constrained();
$table->timestamps();
});
Kod wygląda dobrze, prawda? A co, jeśli tabela "team_leagues", do której następuje odwołanie, nie istnieje? A może nazywa się inaczej? Następnie zobaczysz ten błąd w terminalu:
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`))
Ale to tylko część problemu. Więc ok, zdałeś sobie sprawę, że przywoływana tabela nazywa się "ligi", a nie "team_leagues". Możliwe opcje naprawy:
- Zmień nazwę pola "team_league_id" na "league_id"
- Możesz też określić tabelę
->constrained('leagues')
Ale prawdziwym problemem jest teraz stan bazy danych:
- Tabela
teams
jest już utworzona - Ale obcy klucz do lig zawiódł!
Oznacza to, że nie ma zapisu o tym sukcesie migracji w tabeli "migracje" Laravel system DB.
Teraz prawdziwy problem: jeśli naprawisz błąd w tej samej migracji i po prostu uruchomisz php artisan migrate
, powie "Tabela już istnieje".
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` (...)
Czy należy utworzyć nową migrację? Wycofywanie? Pozwólcie, że wyjaśnię mój ulubiony sposób rozwiązania tego problemu.
Rozwiązanie: Schema::hasTable() i oddzielny klucz
obcy Możesz ponownie uruchomić migrację dla już istniejących tabel i upewnić się, że zostaną utworzone tylko wtedy, gdy nie istnieją z metodą Schema::hasTable()
.
Ale potem musimy podzielić na foreignId()
części, ponieważ jest to w rzeczywistości metoda 2 w 1: tworzy kolumnę (co się powiodło) i klucz obcy (który się nie powiódł).
Tak więc przepisujemy migrację na to:
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');
});
Teraz, jeśli uruchomisz php artisan migrate
, pomyślnie wykona pełną migrację.
Oczywiście alternatywnym rozwiązaniem byłoby ręczne usunięcie teams
tabeli za pomocą klienta SQL i ponowne uruchomienie migracji z poprawką, ale nie zawsze masz dostęp do bazy danych, jeśli jest zdalna. Ponadto nie jest idealnym rozwiązaniem ręczne wykonywanie jakichkolwiek operacji na bazie danych, jeśli używasz migracji. Może to być w porządku w lokalnej bazie danych, ale powyższe rozwiązanie byłoby uniwersalne dla wszystkich lokalnych/zdalnych baz danych.