Total Complexity | 50 |
Total Lines | 323 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like UpdateNNTmuxDB often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use UpdateNNTmuxDB, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class UpdateNNTmuxDB extends Command |
||
10 | { |
||
11 | /** |
||
12 | * The name and signature of the console command. |
||
13 | */ |
||
14 | protected $signature = 'nntmux:db |
||
15 | {--seed : Run database seeders after migration} |
||
16 | {--rollback= : Rollback the last N migrations} |
||
17 | {--check : Check migration status without running}'; |
||
18 | |||
19 | /** |
||
20 | * The console command description. |
||
21 | */ |
||
22 | protected $description = 'Update NNTmux database with enhanced safety and performance'; |
||
23 | |||
24 | /** |
||
25 | * Execute the console command. |
||
26 | */ |
||
27 | public function handle(): int |
||
28 | { |
||
29 | try { |
||
30 | $this->info('🗄️ Starting database update process...'); |
||
31 | |||
32 | // Check database connection and detect database type |
||
33 | $dbType = $this->checkDatabaseConnection(); |
||
34 | if (!$dbType) { |
||
35 | $this->error('Database connection failed'); |
||
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 | // Backup database before migration (in production) |
||
50 | if (app()->environment('production')) { |
||
|
|||
51 | $this->info('📋 Creating database backup...'); |
||
52 | $this->createDatabaseBackup($dbType); |
||
53 | } |
||
54 | |||
55 | // Run migrations |
||
56 | $this->info('🔄 Running database migrations...'); |
||
57 | $this->runMigrations(); |
||
58 | |||
59 | // Run seeders if requested |
||
60 | if ($this->option('seed')) { |
||
61 | $this->info('🌱 Running database seeders...'); |
||
62 | $this->runSeeders(); |
||
63 | } |
||
64 | |||
65 | // Optimize database |
||
66 | $this->info('⚡ Optimizing database...'); |
||
67 | $this->optimizeDatabase($dbType); |
||
68 | |||
69 | $this->info('✅ Database update completed successfully'); |
||
70 | return Command::SUCCESS; |
||
71 | |||
72 | } catch (\Exception $e) { |
||
73 | $this->error('❌ Database update failed: ' . $e->getMessage()); |
||
74 | return Command::FAILURE; |
||
75 | } |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * Check database connection and return database type |
||
80 | */ |
||
81 | private function checkDatabaseConnection(): ?string |
||
82 | { |
||
83 | try { |
||
84 | DB::connection()->getPdo(); |
||
85 | |||
86 | $dbConfig = config('database.connections.' . config('database.default')); |
||
87 | $driver = $dbConfig['driver'] ?? null; |
||
88 | |||
89 | // Detect actual database type for MySQL-compatible drivers |
||
90 | if (in_array($driver, ['mysql', 'mariadb'])) { |
||
91 | $version = DB::select('SELECT VERSION() as version')[0]->version; |
||
92 | |||
93 | if (str_contains(strtolower($version), 'mariadb')) { |
||
94 | $actualType = 'mariadb'; |
||
95 | } else { |
||
96 | $actualType = 'mysql'; |
||
97 | } |
||
98 | |||
99 | $this->line(" ✓ Database connection established ($actualType $version)"); |
||
100 | return $actualType; |
||
101 | } |
||
102 | |||
103 | $this->line(" ✓ Database connection established ($driver)"); |
||
104 | return $driver; |
||
105 | |||
106 | } catch (\Exception $e) { |
||
107 | $this->error(' ✗ Database connection failed: ' . $e->getMessage()); |
||
108 | return null; |
||
109 | } |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Handle migration rollback |
||
114 | */ |
||
115 | private function handleRollback(): int |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Check migration status |
||
142 | */ |
||
143 | private function checkMigrationStatus(): int |
||
144 | { |
||
145 | $this->info('📊 Migration Status:'); |
||
146 | return $this->call('migrate:status'); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Create database backup with proper driver detection |
||
151 | */ |
||
152 | private function createDatabaseBackup(string $dbType): void |
||
153 | { |
||
154 | try { |
||
155 | $backupPath = storage_path('backups'); |
||
156 | |||
157 | if (!is_dir($backupPath)) { |
||
158 | mkdir($backupPath, 0755, true); |
||
159 | } |
||
160 | |||
161 | $timestamp = now()->format('Y-m-d_H-i-s'); |
||
162 | $filename = "nntmux_backup_{$timestamp}.sql"; |
||
163 | $fullPath = $backupPath . '/' . $filename; |
||
164 | |||
165 | $dbConfig = config('database.connections.' . config('database.default')); |
||
166 | |||
167 | if (in_array($dbType, ['mysql', 'mariadb'])) { |
||
168 | $this->createMysqlCompatibleBackup($dbConfig, $fullPath, $dbType); |
||
169 | } elseif ($dbType === 'pgsql') { |
||
170 | $this->createPostgresBackup($dbConfig, $fullPath); |
||
171 | } else { |
||
172 | $this->warn(" ⚠ Backup not supported for database type: $dbType"); |
||
173 | } |
||
174 | |||
175 | } catch (\Exception $e) { |
||
176 | $this->warn(' ⚠ Backup creation failed: ' . $e->getMessage()); |
||
177 | } |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Create backup for MySQL/MariaDB |
||
182 | */ |
||
183 | private function createMysqlCompatibleBackup(array $dbConfig, string $fullPath, string $dbType): void |
||
184 | { |
||
185 | $command = sprintf( |
||
186 | '%s -h%s -P%s -u%s %s %s > %s 2>/dev/null', |
||
187 | $dbType === 'mariadb' ? 'mariadb-dump' : 'mysqldump', |
||
188 | escapeshellarg($dbConfig['host']), |
||
189 | escapeshellarg($dbConfig['port']), |
||
190 | escapeshellarg($dbConfig['username']), |
||
191 | !empty($dbConfig['password']) ? '-p' . escapeshellarg($dbConfig['password']) : '', |
||
192 | escapeshellarg($dbConfig['database']), |
||
193 | escapeshellarg($fullPath) |
||
194 | ); |
||
195 | |||
196 | exec($command, $output, $returnVar); |
||
197 | |||
198 | if ($returnVar === 0 && file_exists($fullPath) && filesize($fullPath) > 0) { |
||
199 | $this->line(" ✓ $dbType backup created: " . basename($fullPath)); |
||
200 | } else { |
||
201 | // Fallback to mysqldump if mariadb-dump failed |
||
202 | if ($dbType === 'mariadb') { |
||
203 | $fallbackCommand = str_replace('mariadb-dump', 'mysqldump', $command); |
||
204 | exec($fallbackCommand, $output, $returnVar); |
||
205 | |||
206 | if ($returnVar === 0 && file_exists($fullPath) && filesize($fullPath) > 0) { |
||
207 | $this->line(" ✓ MySQL backup created (fallback): " . basename($fullPath)); |
||
208 | } else { |
||
209 | $this->warn(' ⚠ Backup creation failed'); |
||
210 | } |
||
211 | } else { |
||
212 | $this->warn(' ⚠ Backup creation failed'); |
||
213 | } |
||
214 | } |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Create backup for PostgreSQL |
||
219 | */ |
||
220 | private function createPostgresBackup(array $dbConfig, string $fullPath): void |
||
238 | } |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Run database migrations |
||
243 | */ |
||
244 | private function runMigrations(): void |
||
245 | { |
||
246 | $migrateOptions = []; |
||
247 | |||
248 | if (app()->environment('production')) { |
||
249 | $migrateOptions['--force'] = true; |
||
250 | } |
||
251 | |||
252 | $exitCode = $this->call('migrate', $migrateOptions); |
||
253 | |||
254 | if ($exitCode !== 0) { |
||
255 | throw new \Exception('Migration failed'); |
||
256 | } |
||
257 | |||
258 | $this->line(' ✓ Migrations completed successfully'); |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Run database seeders |
||
263 | */ |
||
264 | private function runSeeders(): void |
||
265 | { |
||
266 | $seedOptions = []; |
||
267 | |||
268 | if (app()->environment('production')) { |
||
269 | $seedOptions['--force'] = true; |
||
270 | } |
||
271 | |||
272 | $exitCode = $this->call('db:seed', $seedOptions); |
||
273 | |||
274 | if ($exitCode !== 0) { |
||
275 | throw new \Exception('Seeding failed'); |
||
276 | } |
||
277 | |||
278 | $this->line(' ✓ Seeders completed successfully'); |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Optimize database tables with proper driver detection |
||
283 | */ |
||
284 | private function optimizeDatabase(string $dbType): void |
||
285 | { |
||
286 | try { |
||
287 | if (in_array($dbType, ['mysql', 'mariadb'])) { |
||
288 | $this->optimizeMysqlCompatible($dbType); |
||
289 | } elseif ($dbType === 'pgsql') { |
||
290 | $this->optimizePostgres(); |
||
291 | } else { |
||
292 | $this->line(" ℹ Database optimization skipped (unsupported type: $dbType)"); |
||
293 | } |
||
294 | } catch (\Exception $e) { |
||
295 | $this->warn(' ⚠ Database optimization failed: ' . $e->getMessage()); |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Optimize MySQL/MariaDB tables |
||
301 | */ |
||
302 | private function optimizeMysqlCompatible(string $dbType): void |
||
303 | { |
||
304 | $dbConfig = config('database.connections.' . config('database.default')); |
||
305 | $tables = DB::select('SHOW TABLES'); |
||
306 | $tableKey = 'Tables_in_' . $dbConfig['database']; |
||
307 | |||
308 | $optimizedCount = 0; |
||
309 | foreach ($tables as $table) { |
||
310 | $tableName = $table->$tableKey; |
||
311 | try { |
||
312 | DB::statement("OPTIMIZE TABLE `$tableName`"); |
||
313 | $optimizedCount++; |
||
314 | } catch (\Exception $e) { |
||
315 | $this->warn(" ⚠ Failed to optimize table: $tableName"); |
||
316 | } |
||
317 | } |
||
318 | |||
319 | $this->line(" ✓ $dbType tables optimized ($optimizedCount/" . count($tables) . ")"); |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * Optimize PostgreSQL database |
||
324 | */ |
||
325 | private function optimizePostgres(): void |
||
332 | } |
||
333 | } |
||
334 | } |
||
335 |