setupMasterConnection(); $masterMigrations = $this->getMasterMigrationFiles(); if (empty($masterMigrations)) { return ['message' => 'No master migrations found.', 'migrations' => []]; } // Create a temporary directory for master migrations only $tempPath = storage_path('app/temp_master_migrations'); if (!File::exists($tempPath)) { File::makeDirectory($tempPath, 0755, true); } // Copy only master migrations to temp directory foreach ($masterMigrations as $migration) { $source = database_path('migrations/' . $migration); $destination = $tempPath . '/' . $migration; File::copy($source, $destination); } try { // Run migrations from temp directory Artisan::call('migrate', [ '--database' => 'master', '--path' => 'storage/app/temp_master_migrations' ]); $output = Artisan::output(); } finally { // Clean up temp directory File::deleteDirectory($tempPath); } return [ 'message' => 'Master migrations completed successfully!', 'database' => $this->masterDatabase, 'migrations' => $masterMigrations, 'output' => $output ]; } public function runTenantMigrations() { $tenants = $this->getTenants(); $results = []; if ($tenants->isEmpty()) { return ['message' => 'No tenants found.', 'results' => []]; } $tenantMigrations = $this->getTenantMigrationFiles(); if (empty($tenantMigrations)) { return ['message' => 'No tenant migrations found.', 'results' => []]; } // Create a temporary directory for tenant migrations only $tempPath = storage_path('app/temp_tenant_migrations'); if (!File::exists($tempPath)) { File::makeDirectory($tempPath, 0755, true); } // Copy only tenant migrations to temp directory foreach ($tenantMigrations as $migration) { $source = database_path('migrations/' . $migration); $destination = $tempPath . '/' . $migration; File::copy($source, $destination); } foreach ($tenants as $tenant) { try { $this->setupTenantConnection($tenant); Artisan::call('migrate', [ '--database' => 'tenant', '--path' => 'storage/app/temp_tenant_migrations' ]); $results[] = [ 'tenant' => $tenant->tenant_database, 'status' => 'success', 'message' => 'Migrations completed successfully' ]; } catch (\Exception $e) { $results[] = [ 'tenant' => $tenant->tenant_database, 'status' => 'error', 'message' => $e->getMessage() ]; } } // Clean up temp directory File::deleteDirectory($tempPath); return [ 'message' => 'Tenant migrations process completed', 'results' => $results ]; } public function rollbackMasterMigrations($steps = 1) { $this->setupMasterConnection(); Artisan::call('migrate:rollback', [ '--database' => 'master', '--step' => $steps ]); return [ 'message' => "Master database rolled back {$steps} step(s)", 'database' => $this->masterDatabase, 'output' => Artisan::output() ]; } public function rollbackTenantMigrations($steps = 1) { $tenants = $this->getTenants(); $results = []; foreach ($tenants as $tenant) { try { $this->setupTenantConnection($tenant); Artisan::call('migrate:rollback', [ '--database' => 'tenant', '--step' => $steps ]); $results[] = [ 'tenant' => $tenant->tenant_database, 'status' => 'success', 'message' => "Rolled back {$steps} step(s)" ]; } catch (\Exception $e) { $results[] = [ 'tenant' => $tenant->tenant_database, 'status' => 'error', 'message' => $e->getMessage() ]; } } return [ 'message' => 'Tenant rollback process completed', 'results' => $results ]; } protected function setupMasterConnection() { config(['database.connections.master' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => $this->masterDatabase, 'username' => env('DB_USERNAME'), 'password' => env('DB_PASSWORD'), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ]]); config(['database.default' => 'master']); DB::purge('master'); DB::reconnect('master'); } protected function setupTenantConnection($tenant) { config(['database.connections.tenant' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => $tenant->tenant_database, 'username' => $tenant->tenant_username, 'password' => $tenant->tenant_password, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ]]); config(['database.default' => 'tenant']); DB::purge('tenant'); DB::reconnect('tenant'); } protected function getTenants() { return User::select('tenant_database', 'tenant_username', 'tenant_password') ->whereNotNull('tenant_database') ->distinct() ->get(); } protected function getMasterMigrationFiles() { $migrationPath = database_path('migrations'); $files = File::glob($migrationPath . '/*.php'); $masterMigrations = []; foreach ($files as $file) { $content = File::get($file); if (strpos($content, 'extends MasterMigration') !== false) { $masterMigrations[] = basename($file); } } return $masterMigrations; } protected function getTenantMigrationFiles() { $migrationPath = database_path('migrations'); $files = File::glob($migrationPath . '/*.php'); $tenantMigrations = []; foreach ($files as $file) { $content = File::get($file); if (strpos($content, 'extends TenantMigration') !== false) { $tenantMigrations[] = basename($file); } } return $tenantMigrations; } }