Created
October 8, 2024 09:43
-
-
Save mRoca/2c2c2e5ba1d17a7f795f4aee64e1e10a to your computer and use it in GitHub Desktop.
Laravel - Create a migration adding an index for all foreign keys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Console\Commands; | |
use Doctrine\DBAL\Schema\Index; | |
use Illuminate\Console\Command; | |
use Illuminate\Support\Facades\Schema; | |
final class CreateMissingForeignKeysIndexesTestCommand extends Command | |
{ | |
/** @inheritdoc */ | |
protected $signature = 'db:create-missing-indexes-migration {--dry-run}'; | |
/** @inheritdoc */ | |
protected $description = 'Creates a migration adding an index for each foreign key without index'; | |
private string $migrationUpBody = ''; | |
private string $migrationDownBody = ''; | |
/** | |
* Execute the console command. | |
*/ | |
public function handle(): int | |
{ | |
$schemaManager = Schema::getConnection()->getDoctrineSchemaManager(); | |
$tables = $schemaManager->listTables(); | |
$foreignKeysWithIndex = 0; | |
$foreignKeysWithUniqueIndex = 0; | |
$foreignKeysWithoutIndex = 0; | |
foreach ($tables as $table) { | |
$tableName = $table->getName(); | |
$foreignKeys = $schemaManager->listTableForeignKeys($tableName); | |
foreach ($foreignKeys as $foreignKey) { | |
$localColumns = $foreignKey->getLocalColumns(); | |
/** @var Index[] $matchingIndexes */ | |
$matchingIndexes = array_values(array_filter($table->getIndexes(), function (Index $index) use ($localColumns) { | |
// Foreign keys are considered as an index | |
// for SQL Server, indexes are clustered (primary keys), or nonclustered | |
// TOOD Update this line for other Databases | |
if (!$index->isPrimary() && !$index->hasFlag('nonclustered')) { | |
return false; | |
} | |
return $index->spansColumns($localColumns); | |
})); | |
if (!empty($matchingIndexes)) { | |
if ($matchingIndexes[0]->isUnique()) { | |
++$foreignKeysWithUniqueIndex; | |
} else { | |
++$foreignKeysWithIndex; | |
} | |
$firstIndexName = $matchingIndexes[0]->getName(); | |
$this->output->success( | |
"Table [$tableName] already has index on [" . implode(',', $localColumns) . "] : $firstIndexName" | |
); | |
continue; | |
} | |
++$foreignKeysWithoutIndex; | |
$this->output->warning("Table [$tableName] needs an index on [" . implode(',', $localColumns) . "]"); | |
$this->storeIndexToCreate($tableName, $localColumns); | |
} | |
} | |
$this->output->info( | |
"$foreignKeysWithUniqueIndex foreign keys with UNIQUE index, $foreignKeysWithIndex foreign keys with index, $foreignKeysWithoutIndex WITHOUT INDEXES" | |
); | |
if ($this->option('dry-run')) { | |
$this->info('Dry run, skipping migration creation'); | |
return self::SUCCESS; | |
} | |
$fileName = $this->createMigration(); | |
$this->output->comment("File $fileName created with success"); | |
return self::SUCCESS; | |
} | |
private function storeIndexToCreate(string $tableName, array $columns): void | |
{ | |
$columnsName = implode(', ', array_map(static fn(string $col) => "'$col'", $columns)); | |
$this->migrationUpBody .= <<<EOL | |
Schema::table('$tableName', function (Blueprint \$table): void { | |
\$table->index([$columnsName]); | |
}); | |
EOL; | |
$this->migrationDownBody .= <<<EOL | |
Schema::table('$tableName', function (Blueprint \$table): void { | |
\$table->dropIndex([$columnsName]); | |
}); | |
EOL; | |
} | |
private function createMigration(): string | |
{ | |
$curDate = date('Y_m_d_His'); | |
$filename = "database/migrations/${curDate}_add_missing_foreign_keys_indexes.php"; | |
$content = <<<EOL | |
<?php | |
declare(strict_types=1); | |
use Illuminate\Database\Migrations\Migration; | |
use Illuminate\Database\Schema\Blueprint; | |
return new class() extends Migration { | |
public function up(): void | |
{ | |
$this->migrationUpBody | |
} | |
public function down(): void | |
{ | |
$this->migrationDownBody | |
} | |
}; | |
EOL; | |
file_put_contents($filename, $content); | |
return $filename; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment