Passed
Push — master ( 97ef82...270f36 )
by Darko
09:05
created

UpdateNNTmuxDB::runMigrations()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
dl 0
loc 15
rs 10
c 1
b 0
f 0
cc 3
nc 4
nop 0
1
<?php
2
3
namespace App\Console\Commands;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Facades\DB;
7
8
class UpdateNNTmuxDB extends Command
9
{
10
    /**
11
     * The name and signature of the console command.
12
     */
13
    protected $signature = 'nntmux:db
14
                            {--seed : Run database seeders after migration}
15
                            {--rollback= : Rollback the last N migrations}
16
                            {--check : Check migration status without running}';
17
18
    /**
19
     * The console command description.
20
     */
21
    protected $description = 'Update NNTmux database with enhanced safety and performance';
22
23
    /**
24
     * Execute the console command.
25
     */
26
    public function handle(): int
27
    {
28
        try {
29
            $this->info('🗄️ Starting database update process...');
30
31
            // Check database connection and detect database type
32
            $dbType = $this->checkDatabaseConnection();
33
            if (! $dbType) {
34
                $this->error('Database connection failed');
35
36
                return Command::FAILURE;
37
            }
38
39
            // Handle rollback if requested
40
            if ($this->option('rollback')) {
41
                return $this->handleRollback();
42
            }
43
44
            // Check migration status if requested
45
            if ($this->option('check')) {
46
                return $this->checkMigrationStatus();
47
            }
48
49
            // Run migrations
50
            $this->info('🔄 Running database migrations...');
51
            $this->runMigrations();
52
53
            // Run seeders if requested
54
            if ($this->option('seed')) {
55
                $this->info('🌱 Running database seeders...');
56
                $this->runSeeders();
57
            }
58
59
            // Optimize database
60
            $this->info('⚡ Optimizing database...');
61
            $this->optimizeDatabase($dbType);
62
63
            $this->info('✅ Database update completed successfully');
64
65
            return Command::SUCCESS;
66
67
        } catch (\Exception $e) {
68
            $this->error('❌ Database update failed: '.$e->getMessage());
69
70
            return Command::FAILURE;
71
        }
72
    }
73
74
    /**
75
     * Check database connection and return database type
76
     */
77
    private function checkDatabaseConnection(): ?string
78
    {
79
        try {
80
            DB::connection()->getPdo();
81
82
            $dbConfig = config('database.connections.'.config('database.default'));
83
            $driver = $dbConfig['driver'] ?? null;
84
85
            // Detect actual database type for MySQL-compatible drivers
86
            if (in_array($driver, ['mysql', 'mariadb'])) {
87
                $version = DB::select('SELECT VERSION() as version')[0]->version;
88
89
                if (str_contains(strtolower($version), 'mariadb')) {
90
                    $actualType = 'mariadb';
91
                } else {
92
                    $actualType = 'mysql';
93
                }
94
95
                $this->line("  ✓ Database connection established ($actualType $version)");
96
97
                return $actualType;
98
            }
99
100
            $this->line("  ✓ Database connection established ($driver)");
101
102
            return $driver;
103
104
        } catch (\Exception $e) {
105
            $this->error('  ✗ Database connection failed: '.$e->getMessage());
106
107
            return null;
108
        }
109
    }
110
111
    /**
112
     * Handle migration rollback
113
     */
114
    private function handleRollback(): int
115
    {
116
        $steps = (int) $this->option('rollback');
117
118
        if ($steps <= 0) {
119
            $this->error('Invalid rollback steps. Must be a positive integer.');
120
121
            return Command::FAILURE;
122
        }
123
124
        $this->warn("⚠️ Rolling back $steps migrations...");
125
126
        if (! $this->confirm('Are you sure you want to rollback migrations? This may cause data loss.')) {
127
            $this->info('Rollback cancelled');
128
129
            return Command::SUCCESS;
130
        }
131
132
        $exitCode = $this->call('migrate:rollback', ['--step' => $steps]);
133
134
        if ($exitCode === 0) {
135
            $this->info("✅ Successfully rolled back $steps migrations");
136
        }
137
138
        return $exitCode;
139
    }
140
141
    /**
142
     * Check migration status
143
     */
144
    private function checkMigrationStatus(): int
145
    {
146
        $this->info('📊 Migration Status:');
147
148
        return $this->call('migrate:status');
149
    }
150
151
    /**
152
     * Run database migrations
153
     */
154
    private function runMigrations(): void
155
    {
156
        $migrateOptions = [];
157
158
        if (app()->environment('production')) {
0 ignored issues
show
introduced by
The method environment() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
        if (app()->/** @scrutinizer ignore-call */ environment('production')) {
Loading history...
159
            $migrateOptions['--force'] = true;
160
        }
161
162
        $exitCode = $this->call('migrate', $migrateOptions);
163
164
        if ($exitCode !== 0) {
165
            throw new \Exception('Migration failed');
166
        }
167
168
        $this->line('  ✓ Migrations completed successfully');
169
    }
170
171
    /**
172
     * Run database seeders
173
     */
174
    private function runSeeders(): void
175
    {
176
        $seedOptions = [];
177
178
        if (app()->environment('production')) {
179
            $seedOptions['--force'] = true;
180
        }
181
182
        $exitCode = $this->call('db:seed', $seedOptions);
183
184
        if ($exitCode !== 0) {
185
            throw new \Exception('Seeding failed');
186
        }
187
188
        $this->line('  ✓ Seeders completed successfully');
189
    }
190
191
    /**
192
     * Optimize database tables with proper driver detection
193
     */
194
    private function optimizeDatabase(string $dbType): void
195
    {
196
        try {
197
            if (in_array($dbType, ['mysql', 'mariadb'])) {
198
                $this->optimizeMysqlCompatible($dbType);
199
            } elseif ($dbType === 'pgsql') {
200
                $this->optimizePostgres();
201
            } else {
202
                $this->line("  ℹ Database optimization skipped (unsupported type: $dbType)");
203
            }
204
        } catch (\Exception $e) {
205
            $this->warn('  ⚠ Database optimization failed: '.$e->getMessage());
206
        }
207
    }
208
209
    /**
210
     * Optimize MySQL/MariaDB tables
211
     */
212
    private function optimizeMysqlCompatible(string $dbType): void
213
    {
214
        $dbConfig = config('database.connections.'.config('database.default'));
215
        $tables = DB::select('SHOW TABLES');
216
        $tableKey = 'Tables_in_'.$dbConfig['database'];
217
218
        $optimizedCount = 0;
219
        foreach ($tables as $table) {
220
            $tableName = $table->$tableKey;
221
            try {
222
                DB::statement("OPTIMIZE TABLE `$tableName`");
223
                $optimizedCount++;
224
            } catch (\Exception $e) {
225
                $this->warn("    ⚠ Failed to optimize table: $tableName");
226
            }
227
        }
228
229
        $this->line("  ✓ $dbType tables optimized ($optimizedCount/".count($tables).')');
230
    }
231
232
    /**
233
     * Optimize PostgreSQL database
234
     */
235
    private function optimizePostgres(): void
236
    {
237
        try {
238
            DB::statement('VACUUM ANALYZE');
239
            $this->line('  ✓ PostgreSQL database optimized (VACUUM ANALYZE)');
240
        } catch (\Exception $e) {
241
            $this->warn('  ⚠ PostgreSQL optimization failed: '.$e->getMessage());
242
        }
243
    }
244
}
245