debug = $this->option('debug'); if (! $this->isDevelopment()) { $this->error('This command can only be run in development mode.'); return 1; } $filePath = $this->argument('file'); if (! file_exists($filePath)) { $this->error("File not found: {$filePath}"); return 1; } if (! is_readable($filePath)) { $this->error("File is not readable: {$filePath}"); return 1; } try { $this->info('Starting database restoration...'); $database = config('database.connections.pgsql.database'); $host = config('database.connections.pgsql.host'); $port = config('database.connections.pgsql.port'); $username = config('database.connections.pgsql.username'); $password = config('database.connections.pgsql.password'); if (! $database || ! $username) { $this->error('Database configuration is incomplete.'); return 1; } $this->info("Restoring to database: {$database}"); // Drop all tables if (! $this->dropAllTables($database, $host, $port, $username, $password)) { return 1; } // Restore the database dump if (! $this->restoreDatabaseDump($filePath, $database, $host, $port, $username, $password)) { return 1; } $this->info('Database restoration completed successfully!'); return 0; } catch (\Exception $e) { $this->error("An error occurred: {$e->getMessage()}"); return 1; } } private function dropAllTables(string $database, string $host, string $port, string $username, string $password): bool { $this->info('Dropping all tables...'); // SQL to drop all tables $dropTablesSQL = <<<'SQL' DO $$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END $$; SQL; // Build the psql command to drop all tables $command = sprintf( 'PGPASSWORD=%s psql -h %s -p %s -U %s -d %s -c %s', escapeshellarg($password), escapeshellarg($host), escapeshellarg($port), escapeshellarg($username), escapeshellarg($database), escapeshellarg($dropTablesSQL) ); if ($this->debug) { $this->line('Executing drop command:'); $this->line($command); } $output = shell_exec($command.' 2>&1'); if ($this->debug) { $this->line("Output: {$output}"); } $this->info('All tables dropped successfully.'); return true; } private function restoreDatabaseDump(string $filePath, string $database, string $host, string $port, string $username, string $password): bool { $this->info('Restoring database from dump file...'); // Handle gzipped files by decompressing first $actualFile = $filePath; if (str_ends_with($filePath, '.gz')) { $actualFile = rtrim($filePath, '.gz'); $this->info('Decompressing gzipped dump file...'); $decompressCommand = sprintf( 'gunzip -c %s > %s', escapeshellarg($filePath), escapeshellarg($actualFile) ); if ($this->debug) { $this->line('Executing decompress command:'); $this->line($decompressCommand); } $decompressOutput = shell_exec($decompressCommand.' 2>&1'); if ($this->debug && $decompressOutput) { $this->line("Decompress output: {$decompressOutput}"); } } // Use pg_restore for custom format dumps $command = sprintf( 'PGPASSWORD=%s pg_restore -h %s -p %s -U %s -d %s -v %s', escapeshellarg($password), escapeshellarg($host), escapeshellarg($port), escapeshellarg($username), escapeshellarg($database), escapeshellarg($actualFile) ); if ($this->debug) { $this->line('Executing restore command:'); $this->line($command); } // Execute the restore command $process = proc_open( $command, [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes ); if (! is_resource($process)) { $this->error('Failed to start restoration process.'); return false; } $output = stream_get_contents($pipes[1]); $error = stream_get_contents($pipes[2]); $exitCode = proc_close($process); // Clean up decompressed file if we created one if ($actualFile !== $filePath && file_exists($actualFile)) { unlink($actualFile); } if ($this->debug) { if ($output) { $this->line('Output:'); $this->line($output); } if ($error) { $this->line('Error output:'); $this->line($error); } $this->line("Exit code: {$exitCode}"); } if ($exitCode !== 0) { $this->error("Restoration failed with exit code: {$exitCode}"); if ($error) { $this->error('Error details:'); $this->error($error); } return false; } if ($output && ! $this->debug) { $this->line($output); } return true; } private function isDevelopment(): bool { return app()->environment(['local', 'development', 'dev']); } }