execute(" CREATE TABLE IF NOT EXISTS migrations ( id INT AUTO_INCREMENT PRIMARY KEY, filename VARCHAR(255) NOT NULL UNIQUE, executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, KEY idx_filename (filename) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 "); echo "✅ Tabla de control de migraciones verificada\n"; // Obtener lista de migraciones ejecutadas $executed = $db->fetchAll("SELECT filename FROM migrations"); $executedFiles = array_column($executed, 'filename'); // Buscar archivos .sql en database/ Y en migrations/ (carpeta raíz del proyecto) $dbFiles = glob(__DIR__ . '/*.sql') ?: []; $migrationsFiles = glob(__DIR__ . '/../migrations/*.sql') ?: []; // Unir y ordenar por nombre de archivo (orden cronológico por prefijo) $allFiles = array_unique(array_merge($dbFiles, $migrationsFiles)); usort($allFiles, fn($a, $b) => strcmp(basename($a), basename($b))); $migrationFiles = $allFiles; if (empty($migrationFiles)) { echo "ℹ️ No se encontraron archivos de migración\n"; exit(0); } echo "📋 Encontrados " . count($migrationFiles) . " archivos de migración\n"; echo " (database/: " . count($dbFiles) . ", migrations/: " . count($migrationsFiles) . ")\n\n"; $executed_count = 0; $skipped_count = 0; foreach ($migrationFiles as $file) { $filename = basename($file); // Saltar archivo de backup completo if (strpos($filename, 'backup') !== false) { continue; } // Verificar si ya fue ejecutada if (in_array($filename, $executedFiles)) { // Verificación extra: si el SQL crea tablas, comprobar que realmente existan. // Si alguna tabla del archivo no existe, re-ejecutar la migración. $sqlRaw = file_get_contents($file); preg_match_all( '/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?(\w+)`?/i', $sqlRaw, $tableMatches ); $missingTable = false; foreach ($tableMatches[1] as $tableName) { $rows = $db->fetchAll( "SELECT COUNT(*) as cnt FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?", [$tableName] ); if (empty($rows) || ($rows[0]['cnt'] ?? 0) == 0) { $missingTable = $tableName; break; } } if ($missingTable) { echo "⚠️ $filename registrada pero tabla '$missingTable' no existe — re-ejecutando\n"; // Quitar del registro para re-ejecutar $db->execute("DELETE FROM migrations WHERE filename = ?", [$filename]); $executedFiles = array_diff($executedFiles, [$filename]); // Continúa a la ejecución abajo } else { echo "⏭️ Saltando $filename (ya ejecutada)\n"; $skipped_count++; continue; } } echo "🔄 Ejecutando migración: $filename\n"; try { $sql = file_get_contents($file); // Eliminar comentarios de bloque /* ... */ $sql = preg_replace('/\/\*.*?\*\//s', '', $sql); // Dividir en sentencias por ; y limpiar $rawStatements = explode(';', $sql); $statements = []; foreach ($rawStatements as $stmt) { // Eliminar líneas de comentario -- y líneas vacías $lines = explode("\n", $stmt); $cleaned = []; foreach ($lines as $line) { $trimmed = trim($line); if ($trimmed !== '' && strpos($trimmed, '--') !== 0) { $cleaned[] = $line; } } $stmt = trim(implode("\n", $cleaned)); if ($stmt !== '') { $statements[] = $stmt; } } $stmt_count = 0; $had_error = false; foreach ($statements as $stmt) { try { $db->execute($stmt); $stmt_count++; } catch (Exception $e) { $msg = $e->getMessage(); // Ignorar errores de "ya existe" — son idempotentes $ignorable = [ 'Duplicate column', 'already exists', 'Duplicate key name', 'Multiple primary key defined', ]; $ignore = false; foreach ($ignorable as $pattern) { if (strpos($msg, $pattern) !== false) { $ignore = true; break; } } if ($ignore) { echo " ⚠ Ya existe (ignorado): " . substr($msg, 0, 80) . "\n"; continue; } echo " ✗ Error en sentencia: $msg\n"; $had_error = true; // Continuar con las demás sentencias del archivo } } if (!$had_error) { // Registrar migración como ejecutada solo si no hubo errores reales $db->execute( "INSERT INTO migrations (filename) VALUES (?)", [$filename] ); echo " ✓ Ejecutadas $stmt_count sentencias SQL — registrada\n"; } else { echo " ⚠ Migración con errores — NO registrada (se reintentará en el próximo deploy)\n"; } $executed_count++; } catch (Exception $e) { echo " ✗ Error fatal en $filename: " . $e->getMessage() . "\n"; // Continuar con la siguiente migración } } echo "\n"; echo "═══════════════════════════════════════════\n"; echo "✅ Migraciones completadas\n"; echo " - Ejecutadas: $executed_count\n"; echo " - Saltadas: $skipped_count\n"; echo "═══════════════════════════════════════════\n"; } catch (Exception $e) { echo "❌ Error fatal en sistema de migraciones: " . $e->getMessage() . "\n"; exit(1); } exit(0);