masterConnection === null) { $config = SystemConfig::getMasterDatabaseConfig(); try { $this->masterConnection = new mysqli( $config['host'], $config['user'], $config['password'], $config['database'] ); if ($this->masterConnection->connect_error) { throw new Exception("Error conexión master DB: " . $this->masterConnection->connect_error); } $this->masterConnection->set_charset($config['charset']); } catch (Exception $e) { // Crear una conexión falsa para desarrollo sin BD error_log("WARNING: No hay conexión a base de datos. Usando modo desarrollo. " . $e->getMessage()); $this->masterConnection = new MockDatabase(); } } return $this->masterConnection; } /** * Obtiene conexión para empresa específica * @param int $empresaId ID de la empresa * @return mysqli */ public function getEmpresaConnection($empresaId) { if (!isset($this->connections[$empresaId])) { $config = SystemConfig::getEmpresaDatabaseConfig($empresaId); // Validar que exista la base de datos (con fallback) if (!SystemConfig::validateDatabaseExists($config['database'])) { // Intentar fallback a base de datos master $masterConfig = SystemConfig::getMasterDatabaseConfig(); error_log("Base de datos {$config['database']} no encontrada, usando fallback a master"); $config = array_merge($config, [ 'database' => $masterConfig['database'], 'user' => $masterConfig['user'], 'password' => $masterConfig['password'] ]); } $mysqli = new mysqli( $config['host'] . ':' . $config['port'], $config['user'], $config['password'], $config['database'] ); if ($mysqli->connect_error) { throw new Exception("Error conexión empresa $empresaId: " . $mysqli->connect_error); } $mysqli->set_charset($config['charset']); $this->connections[$empresaId] = $mysqli; } return $this->connections[$empresaId]; } /** * Establece empresaId actual basado en usuario en sesión * @param int $userId ID del usuario en sesión */ public function setEmpresaByUser($userId) { $this->currentEmpresaId = SystemConfig::getEmpresaIdByUserId($userId); if (!$this->currentEmpresaId) { throw new Exception("No se pudo determinar empresaId para usuario: $userId"); } } /** * Establece empresaId actual directamente * @param int $empresaId ID de la empresa */ public function setEmpresaId($empresaId) { $this->currentEmpresaId = (int)$empresaId; } /** * Obtiene empresaId actual * @return int|null */ public function getEmpresaId() { return $this->currentEmpresaId; } /** * Obtiene conexión para empresa actual * @return mysqli */ public function getCurrentConnection() { if (!$this->currentEmpresaId) { throw new Exception("No hay empresaId establecido. Use setEmpresaByUser() o setEmpresaId()"); } return $this->getEmpresaConnection($this->currentEmpresaId); } /** * Cierra todas las conexiones */ public function closeAll() { if ($this->masterConnection) { $this->masterConnection->close(); $this->masterConnection = null; } foreach ($this->connections as $connection) { $connection->close(); } $this->connections = []; $this->currentEmpresaId = null; } /** * Destructor para asegurar cierre de conexiones */ public function __destruct() { $this->closeAll(); } } /** * Clase ModernDB compatible con código existente * Facade sobre DatabaseManager para mantener compatibilidad */ class ModernDB { private $connection; private $isMaster = false; private $empresaId = null; /** * Constructor * @param bool $useMaster Si true, usa conexión master * @param int $empresaId ID de la empresa (si no es master) */ public function __construct($useMaster = false, $empresaId = null) { $dbManager = DatabaseManager::getInstance(); if ($useMaster) { $this->connection = $dbManager->getMasterConnection(); $this->isMaster = true; } else { if ($empresaId === null) { $empresaId = $dbManager->getEmpresaId(); } if ($empresaId === null) { throw new Exception("Se requiere empresaId para conexión de empresa"); } $this->connection = $dbManager->getEmpresaConnection($empresaId); $this->empresaId = $empresaId; } } /** * Ejecuta una consulta SQL * @param string $sql Consulta SQL * @return mysqli_result|false */ public function Query($sql) { $result = $this->connection->query($sql); if ($result === false) { error_log("Error en consulta: " . $this->connection->error); error_log("SQL: " . $sql); } return $result; } /** * Obtiene una fila como arreglo asociativo * @param mysqli_result $result Resultado de consulta * @return array|null */ public function FetchAssoc($result) { return $result->fetch_assoc(); } /** * Obtiene el número de filas de un resultado * @param mysqli_result $result Resultado de consulta * @return int */ public function NumRows($result) { return $result->num_rows; } /** * Obtiene un valor específico de una fila * @param mysqli_result $result Resultado de consulta * @param int $row Número de fila * @param mixed $field Campo (nombre o índice) * @return mixed */ public function Result($result, $row, $field = 0) { $result->data_seek($row); $row_data = $result->fetch_array(); return is_numeric($field) ? $row_data[$field] : $row_data[$field]; } /** * Obtiene el último ID insertado * @return int */ public function InsertId() { return $this->connection->insert_id; } /** * Obtiene el número de filas afectadas * @return int */ public function AffectedRows() { return $this->connection->affected_rows; } /** * Libera memoria del resultado * @param mysqli_result $result Resultado a liberar */ public function FreeResult($result) { $result->free(); } /** * Escapa caracteres especiales para prevenir SQL injection * @param string $string String a escapar * @return string */ public function Escape($string) { return $this->connection->real_escape_string($string); } /** * Obtiene el último error de MySQL * @return string */ public function GetError() { return $this->connection->error; } /** * Inicia una transacción */ public function BeginTransaction() { $this->connection->begin_transaction(); } /** * Confirma una transacción */ public function Commit() { $this->connection->commit(); } /** * Revierte una transacción */ public function Rollback() { $this->connection->rollback(); } /** * Cierra la conexión */ public function Close() { // No cerramos conexiones individuales ya que las maneja DatabaseManager } }