Files
hstream/database/migrations/2026_01_08_213625_fix_database_structure.php
2026-01-09 13:01:53 +01:00

208 lines
6.3 KiB
PHP

<?php
use App\Models\Playlist;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Create new column discord_id
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('discord_id')->nullable()->after('id');
});
// 2. Migrate Discord Users IDs
DB::table('users')
->where('id', '>', 10000)
->update(['discord_id' => DB::raw('id')]);
// 3. Temporary new auto increment column
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('new_id')->first();
});
// 3.5 Count
DB::statement('
UPDATE users u
JOIN (
SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS rn
FROM users
) t ON u.id = t.id
SET u.new_id = t.rn
');
// 4. Drop foreign keys
$this->dropForeignKeys();
// 5. Fix ID's in other tables
$this->updateUserIDsInOtherTables();
// 6. Remove old ID
Schema::table('users', function (Blueprint $table) {
$table->bigInteger('id')->unsigned()->change();
$table->dropPrimary('id');
$table->dropColumn('id');
});
// 7. Rename new_id to id
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('new_id', 'id');
});
// 8. Change new ID to auto increment and set as primary key
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('id')->autoIncrement()->primary()->change();
});
// 9. Remove data that would conflict with constraints
$this->deleteUnreferencedData();
// 9. Recreate foreign key constraints
$this->addForeignKeys();
}
/**
* Drop Foreign Keys referencing the user id
*/
private function dropForeignKeys(): void
{
Schema::table('markable_likes', function (Blueprint $table) {
$table->dropForeign(['user_id']);
});
Schema::table('watched', function (Blueprint $table) {
$table->dropForeign(['user_id']);
});
// Our Schema does include a foreign key, for whatever reason it doesn't exist in the first palce
// Schema::table('user_downloads', function (Blueprint $table) {
// $table->dropForeign(['user_id']);
// });
}
/**
* Tables to fix the IDs:
* - comments ['commenter_id']
* - markable_likes ['user_id']
* - notifications ['notifiable_id']
* - playlists ['user_id']
* - user_downloads ['user_id']
* - watched ['user_id']
*/
private function updateUserIDsInOtherTables(): void
{
DB::statement('
UPDATE comments c
JOIN users u ON c.commenter_id = u.id
SET c.commenter_id = u.new_id
');
DB::statement('
UPDATE watched w
JOIN users u ON w.user_id = u.id
SET w.user_id = u.new_id
');
DB::statement('
UPDATE markable_likes ml
JOIN users u ON ml.user_id = u.id
SET ml.user_id = u.new_id
');
DB::statement('
UPDATE notifications n
JOIN users u ON n.notifiable_id = u.id
SET n.notifiable_id = u.new_id
');
DB::statement('
UPDATE playlists p
JOIN users u ON p.user_id = u.id
SET p.user_id = u.new_id
');
DB::statement('
UPDATE user_downloads ud
JOIN users u ON ud.user_id = u.id
SET ud.user_id = u.new_id
');
}
/**
* Due to incorrect handling of user deletes,
* we have unreferenced data
*/
private function deleteUnreferencedData(): void
{
// User Downloads Table
DB::table('user_downloads')
->where('user_id', '>', 1_000_000)
->delete();
// User Playlists Table
$playlists = Playlist::where('user_id', '>', 1_000_000)
->get();
foreach($playlists as $playlist) {
DB::table('playlist_episodes')
->where('playlist_id', '=', $playlist->id)
->delete();
$playlist->delete();
}
}
/**
* Re-Add Foreign Keys to tables which we dropped previously
*/
private function addForeignKeys(): void
{
Schema::table('markable_likes', function (Blueprint $table) {
// Ensure the column is unsigned
$table->bigInteger('user_id')->unsigned()->change();
// Add the foreign key constraint
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::table('watched', function (Blueprint $table) {
// Ensure the column is unsigned
$table->bigInteger('user_id')->unsigned()->change();
// Add the foreign key constraint
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::table('user_downloads', function (Blueprint $table) {
// Ensure the column is unsigned
$table->bigInteger('user_id')->unsigned()->change();
// Add the foreign key constraint
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::table('playlist_episodes', function (Blueprint $table) {
// Ensure the column is unsigned
$table->bigInteger('playlist_id')->unsigned()->change();
// Add the foreign key constraint
$table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade');
});
Schema::table('playlists', function (Blueprint $table) {
// Ensure the column is unsigned
$table->bigInteger('user_id')->unsigned()->change();
// Add the foreign key constraint
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
};