From 5aad43f2e347ccc61df57ced90e29984da1b6ea3 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Thu, 17 Sep 2026 16:13:06 -0300 Subject: [PATCH] feat(migration): enhance foreign key constraints and ensure migration integrity --- .../2026_09_17_000000_split_website_types.php | 61 +++++++++++++++---- .../SplitWebsiteTypesMigrationTest.php | 1 + 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/database/migrations/2026_09_17_000000_split_website_types.php b/database/migrations/2026_09_17_000000_split_website_types.php index b42eaf9..d39d183 100644 --- a/database/migrations/2026_09_17_000000_split_website_types.php +++ b/database/migrations/2026_09_17_000000_split_website_types.php @@ -9,6 +9,14 @@ return new class extends Migration { public function up(): void { + // MySQL keeps completed DDL statements when a later statement fails. + // Resume after the table and column renames without repeating the data copy. + if (! Schema::hasTable('website_type') && Schema::hasTable('admin_website_types')) { + $this->addTypeForeignKeys(); + + return; + } + Schema::create('storefront_website_types', function (Blueprint $table): void { $table->id(); $table->string('codigo')->unique(); @@ -84,20 +92,47 @@ return new class extends Migration Schema::enableForeignKeyConstraints(); } - Schema::table('tenants', function (Blueprint $table): void { - if (DB::getDriverName() !== 'sqlite') { + $this->addTypeForeignKeys(); + } + + private function addTypeForeignKeys(): void + { + if (DB::getDriverName() !== 'sqlite' && ! $this->hasForeignKey('tenants', 'admin_website_type_code')) { + Schema::table('tenants', function (Blueprint $table): void { $table->foreign('admin_website_type_code')->references('codigo')->on('admin_website_types')->cascadeOnUpdate()->nullOnDelete(); - } - $table->foreign('storefront_website_type_code')->references('codigo')->on('storefront_website_types')->cascadeOnUpdate()->nullOnDelete(); - }); - if (DB::getDriverName() !== 'sqlite') { - Schema::table('storefront_website_type_extras', function (Blueprint $table): void { - $table->foreign('storefront_website_type_code')->references('codigo')->on('storefront_website_types')->cascadeOnUpdate()->cascadeOnDelete(); - }); - Schema::table('admin_website_type_integrations', function (Blueprint $table): void { - $table->foreign('admin_website_type_code')->references('codigo')->on('admin_website_types')->cascadeOnDelete(); }); } + + if (! $this->hasForeignKey('tenants', 'storefront_website_type_code')) { + Schema::table('tenants', function (Blueprint $table): void { + $table->foreign('storefront_website_type_code')->references('codigo')->on('storefront_website_types')->cascadeOnUpdate()->nullOnDelete(); + }); + } + + if (DB::getDriverName() !== 'sqlite') { + if (! $this->hasForeignKey('storefront_website_type_extras', 'storefront_website_type_code')) { + Schema::table('storefront_website_type_extras', function (Blueprint $table): void { + $table->foreign('storefront_website_type_code', 'swte_storefront_type_fk')->references('codigo')->on('storefront_website_types')->cascadeOnUpdate()->cascadeOnDelete(); + }); + } + + if (! $this->hasForeignKey('admin_website_type_integrations', 'admin_website_type_code')) { + Schema::table('admin_website_type_integrations', function (Blueprint $table): void { + $table->foreign('admin_website_type_code', 'awti_admin_type_fk')->references('codigo')->on('admin_website_types')->cascadeOnDelete(); + }); + } + } + } + + private function hasForeignKey(string $table, string $column): bool + { + foreach (Schema::getForeignKeys($table) as $foreignKey) { + if ($foreignKey['columns'] === [$column]) { + return true; + } + } + + return false; } public function down(): void @@ -110,8 +145,8 @@ return new class extends Migration $table->dropForeign(['admin_website_type_code']); $table->dropForeign(['storefront_website_type_code']); }); - Schema::table('storefront_website_type_extras', fn (Blueprint $table) => $table->dropForeign(['storefront_website_type_code'])); - Schema::table('admin_website_type_integrations', fn (Blueprint $table) => $table->dropForeign(['admin_website_type_code'])); + Schema::table('storefront_website_type_extras', fn (Blueprint $table) => $table->dropForeign('swte_storefront_type_fk')); + Schema::table('admin_website_type_integrations', fn (Blueprint $table) => $table->dropForeign('awti_admin_type_fk')); Schema::table('tenants', fn (Blueprint $table) => $table->renameColumn('admin_website_type_code', 'website_type_code')); Schema::table('storefront_website_type_extras', fn (Blueprint $table) => $table->renameColumn('storefront_website_type_code', 'website_type_code')); diff --git a/tests/Feature/Migrations/SplitWebsiteTypesMigrationTest.php b/tests/Feature/Migrations/SplitWebsiteTypesMigrationTest.php index 32264aa..3f2ff00 100644 --- a/tests/Feature/Migrations/SplitWebsiteTypesMigrationTest.php +++ b/tests/Feature/Migrations/SplitWebsiteTypesMigrationTest.php @@ -66,6 +66,7 @@ class SplitWebsiteTypesMigrationTest extends TestCase $migration = require database_path('migrations/2026_09_17_000000_split_website_types.php'); $migration->up(); + $migration->up(); $this->assertDatabaseHas('tenants', ['id' => 1, 'admin_website_type_code' => 'onticket', 'storefront_website_type_code' => 'onticket', 'site_title' => 'Old title', 'favicon_id' => 42]); $this->assertDatabaseHas('tenants', ['id' => 2, 'admin_website_type_code' => null, 'storefront_website_type_code' => null]);