Migration shopping_items: idempotent (Duplikat-Spalten, MySQL-Check, Repair)
Made-with: Cursor
This commit is contained in:
parent
7e01043c99
commit
635a0ec28a
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
@ -9,15 +10,43 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('shopping_list_id')->nullable();
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
});
|
||||
if (! Schema::hasTable('shopping_items')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addColumnUnlessExists('shopping_list_id');
|
||||
$this->addColumnUnlessExists('created_by');
|
||||
|
||||
$userIds = DB::table('users')->pluck('id');
|
||||
$map = [];
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$pivot = DB::table('shopping_list_user')
|
||||
->where('user_id', $userId)
|
||||
->orderBy('shopping_list_id')
|
||||
->first();
|
||||
|
||||
if ($pivot !== null) {
|
||||
$map[$userId] = $pivot->shopping_list_id;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$owned = DB::table('shopping_lists')->where('owner_id', $userId)->orderBy('id')->first();
|
||||
if ($owned !== null) {
|
||||
$map[$userId] = $owned->id;
|
||||
if (! DB::table('shopping_list_user')->where('shopping_list_id', $owned->id)->where('user_id', $userId)->exists()) {
|
||||
DB::table('shopping_list_user')->insert([
|
||||
'shopping_list_id' => $owned->id,
|
||||
'user_id' => $userId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$map[$userId] = DB::table('shopping_lists')->insertGetId([
|
||||
'owner_id' => $userId,
|
||||
'name' => 'Einkaufsliste',
|
||||
@ -32,33 +61,111 @@ return new class extends Migration
|
||||
]);
|
||||
}
|
||||
|
||||
$items = DB::table('shopping_items')->select('id', 'user_id')->get();
|
||||
foreach ($items as $item) {
|
||||
if (! isset($map[$item->user_id])) {
|
||||
continue;
|
||||
if (Schema::hasColumn('shopping_items', 'user_id')) {
|
||||
$items = DB::table('shopping_items')->select('id', 'user_id')->get();
|
||||
foreach ($items as $item) {
|
||||
if (! isset($map[$item->user_id])) {
|
||||
continue;
|
||||
}
|
||||
DB::table('shopping_items')->where('id', $item->id)->update([
|
||||
'shopping_list_id' => $map[$item->user_id],
|
||||
'created_by' => $item->user_id,
|
||||
]);
|
||||
}
|
||||
DB::table('shopping_items')->where('id', $item->id)->update([
|
||||
'shopping_list_id' => $map[$item->user_id],
|
||||
'created_by' => $item->user_id,
|
||||
]);
|
||||
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropColumn('user_id');
|
||||
});
|
||||
}
|
||||
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropColumn('user_id');
|
||||
});
|
||||
if (Schema::getConnection()->getDriverName() === 'mysql') {
|
||||
$col = DB::select("SHOW COLUMNS FROM shopping_items WHERE Field = 'shopping_list_id'");
|
||||
if (! empty($col) && ($col[0]->Null ?? '') === 'YES') {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('shopping_list_id')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('shopping_list_id')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('shopping_list_id')->nullable(false)->change();
|
||||
$table->unsignedBigInteger('created_by')->nullable(false)->change();
|
||||
});
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->foreign('shopping_list_id')->references('id')->on('shopping_lists')->cascadeOnDelete();
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->foreign('shopping_list_id')->references('id')->on('shopping_lists')->cascadeOnDelete();
|
||||
$table->foreign('created_by')->references('id')->on('users')->nullOnDelete();
|
||||
$table->index(['shopping_list_id', 'is_done']);
|
||||
$table->index(['shopping_list_id', 'store_id']);
|
||||
});
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->foreign('created_by')->references('id')->on('users')->nullOnDelete();
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->index(['shopping_list_id', 'is_done']);
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->index(['shopping_list_id', 'store_id']);
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
private function addColumnUnlessExists(string $column): void
|
||||
{
|
||||
if ($this->columnExistsMySql('shopping_items', $column)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Schema::hasColumn('shopping_items', $column)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) use ($column) {
|
||||
$table->unsignedBigInteger($column)->nullable();
|
||||
});
|
||||
} catch (QueryException $e) {
|
||||
if ($this->isDuplicateColumnError($e)) {
|
||||
return;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function columnExistsMySql(string $table, string $column): bool
|
||||
{
|
||||
if (Schema::getConnection()->getDriverName() !== 'mysql') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$db = Schema::getConnection()->getDatabaseName();
|
||||
$n = DB::selectOne(
|
||||
'SELECT COUNT(*) AS c FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME = ?',
|
||||
[$db, $table, $column]
|
||||
);
|
||||
|
||||
return $n !== null && (int) $n->c > 0;
|
||||
}
|
||||
|
||||
private function isDuplicateColumnError(QueryException $e): bool
|
||||
{
|
||||
if ($e->getCode() === '42S21') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return str_contains($e->getMessage(), 'Duplicate column');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Repariert fehlgeschlagene Laeufe von 2026_03_29_100100 (MySQL errno 150):
|
||||
* NOT NULL + ON DELETE SET NULL ist ungueltig. Nur ausfuehren wenn created_by noch NOT NULL ist.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable('shopping_items') || ! Schema::hasColumn('shopping_items', 'created_by')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Schema::getConnection()->getDriverName() !== 'mysql') {
|
||||
return;
|
||||
}
|
||||
|
||||
$col = DB::select("SHOW COLUMNS FROM shopping_items WHERE Field = 'created_by'");
|
||||
if (empty($col) || ($col[0]->Null ?? '') === 'YES') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->dropForeign(['created_by']);
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
DB::statement('ALTER TABLE shopping_items MODIFY created_by BIGINT UNSIGNED NULL');
|
||||
|
||||
try {
|
||||
Schema::table('shopping_items', function (Blueprint $table) {
|
||||
$table->foreign('created_by')->references('id')->on('users')->nullOnDelete();
|
||||
});
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user