- Eliminar comentarios de bloque /* */ antes de parsear - Limpiar líneas vacías y comentarios -- sentencia por sentencia - Agregar 'Duplicate key name' y 'Multiple primary key defined' a errores ignorables - Solo registrar en tabla migrations si no hubo errores reales - Si una sentencia falla, continuar con las demás pero marcar had_error=true
167 lines
5.9 KiB
PHP
167 lines
5.9 KiB
PHP
<?php
|
||
/**
|
||
* Sistema de migraciones automáticas
|
||
* Se ejecuta al iniciar el contenedor Docker
|
||
* Fecha: 3 de febrero de 2026
|
||
*/
|
||
|
||
// No mostrar errores en pantalla
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', '0');
|
||
|
||
echo "🔍 Iniciando sistema de migraciones...\n";
|
||
|
||
// Cargar configuración
|
||
if (!file_exists(__DIR__ . '/../config/config.php')) {
|
||
echo "❌ Error: config.php no encontrado\n";
|
||
exit(1);
|
||
}
|
||
|
||
require_once __DIR__ . '/../config/config.php';
|
||
|
||
try {
|
||
$db = Database::getInstance();
|
||
echo "✅ Conexión a base de datos establecida\n";
|
||
|
||
// Crear tabla de control de migraciones si no existe
|
||
$db->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)) {
|
||
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);
|