Files
2026-02-26 14:26:07 +01:00

251 lines
7.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class UpdateNNTmuxDB extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'nntmux:db
{--seed : Run database seeders after migration}
{--rollback= : Rollback the last N migrations}
{--check : Check migration status without running}';
/**
* The console command description.
*/
protected $description = 'Update NNTmux database with enhanced safety and performance';
/**
* Execute the console command.
*/
public function handle(): int
{
try {
$this->info('🗄️ Starting database update process...');
// Check database connection and detect database type
$dbType = $this->checkDatabaseConnection();
if (! $dbType) {
$this->error('Database connection failed');
return Command::FAILURE;
}
// Handle rollback if requested
if ($this->option('rollback')) {
return $this->handleRollback();
}
// Check migration status if requested
if ($this->option('check')) {
return $this->checkMigrationStatus();
}
// Run migrations
$this->info('🔄 Running database migrations...');
$this->runMigrations();
// Run seeders if requested
if ($this->option('seed')) {
$this->info('🌱 Running database seeders...');
$this->runSeeders();
}
/* <!-- disabled for now, can be re-enabled later -->
// Optimize database
$this->info('⚡ Optimizing database...');
$this->optimizeDatabase($dbType);
*/
$this->info('✅ Database update completed successfully');
return Command::SUCCESS;
} catch (\Exception $e) {
$this->error('❌ Database update failed: '.$e->getMessage());
return Command::FAILURE;
}
}
/**
* Check database connection and return database type
*/
private function checkDatabaseConnection(): ?string
{
try {
DB::connection()->getPdo();
$dbConfig = config('database.connections.'.config('database.default'));
$driver = $dbConfig['driver'] ?? null;
// Detect actual database type for MySQL-compatible drivers
if (in_array($driver, ['mysql', 'mariadb'], true)) {
$version = DB::select('SELECT VERSION() as version')[0]->version;
if (str_contains(strtolower($version), 'mariadb')) {
$actualType = 'mariadb';
} else {
$actualType = 'mysql';
}
$this->line(" ✓ Database connection established ($actualType $version)");
return $actualType;
}
$this->line(" ✓ Database connection established ($driver)");
return $driver;
} catch (\Exception $e) {
$this->error(' ✗ Database connection failed: '.$e->getMessage());
return null;
}
}
/**
* Handle migration rollback
*/
private function handleRollback(): int
{
$steps = (int) $this->option('rollback');
if ($steps <= 0) {
$this->error('Invalid rollback steps. Must be a positive integer.');
return Command::FAILURE;
}
$this->warn("⚠️ Rolling back $steps migrations...");
if (! $this->confirm('Are you sure you want to rollback migrations? This may cause data loss.')) {
$this->info('Rollback cancelled');
return Command::SUCCESS;
}
$exitCode = $this->call('migrate:rollback', ['--step' => $steps]);
if ($exitCode === 0) {
$this->info("✅ Successfully rolled back $steps migrations");
}
return $exitCode;
}
/**
* Check migration status
*/
private function checkMigrationStatus(): int
{
$this->info('📊 Migration Status:');
return $this->call('migrate:status');
}
/**
* Run database migrations
*/
private function runMigrations(): void
{
$migrateOptions = [];
if (app()->environment('production')) {
$migrateOptions['--force'] = true;
}
$exitCode = $this->call('migrate', $migrateOptions);
if ($exitCode !== 0) {
throw new \Exception('Migration failed');
}
$this->line(' ✓ Migrations completed successfully');
}
/**
* Run database seeders
*/
private function runSeeders(): void
{
$seedOptions = [];
if (app()->environment('production')) {
$seedOptions['--force'] = true;
}
$exitCode = $this->call('db:seed', $seedOptions);
if ($exitCode !== 0) {
throw new \Exception('Seeding failed');
}
$this->line(' ✓ Seeders completed successfully');
}
/**
* Optimize database tables with proper driver detection
*
* @phpstan-ignore method.unused
*/
private function optimizeDatabase(string $dbType): void
{
try {
if (in_array($dbType, ['mysql', 'mariadb'], true)) {
$this->optimizeMysqlCompatible($dbType);
} elseif ($dbType === 'pgsql') {
$this->optimizePostgres();
} else {
$this->line(" Database optimization skipped (unsupported type: $dbType)");
}
} catch (\Exception $e) {
$this->warn(' ⚠ Database optimization failed: '.$e->getMessage());
}
}
/**
* Optimize MySQL/MariaDB tables
*/
private function optimizeMysqlCompatible(string $dbType): void
{
$dbConfig = config('database.connections.'.config('database.default'));
$tables = DB::select('SHOW TABLES');
$tableKey = 'Tables_in_'.$dbConfig['database'];
$optimizedCount = 0;
foreach ($tables as $table) {
$tableName = $table->$tableKey;
try {
DB::statement("OPTIMIZE TABLE `$tableName`");
$optimizedCount++;
} catch (\Exception $e) {
$this->warn(" ⚠ Failed to optimize table: $tableName");
}
}
$this->line(" ✓ $dbType tables optimized ($optimizedCount/".count($tables).')');
}
/**
* Optimize PostgreSQL database
*/
private function optimizePostgres(): void
{
try {
DB::statement('VACUUM ANALYZE');
$this->line(' ✓ PostgreSQL database optimized (VACUUM ANALYZE)');
} catch (\Exception $e) {
$this->warn(' ⚠ PostgreSQL optimization failed: '.$e->getMessage());
}
}
}