container = $container; return $this->container; } /** * @return ContainerInterface Retorna un objeto que implementa la interfaz "ContainerInterface". */ public function getContainer() { return $this->container; } /** * @return bool Retorna el valor de la variable showParameters. */ public function isShowParameters() { return $this->showParameters; } /** * @param bool $showParameters Si esta en TRUE muestra los parametros en las consultas. */ public function setShowParameters($showParameters) { $this->showParameters = $showParameters; } /** * @return int Retorna el numero de linea que estoy analizando. */ public function getLine() { return $this->line; } /** * @param int $line Setea el numero de linea. */ private function setLine($line) { $this->line = $line; } /** * Funcion que suma 1 a la variable line. */ private function sumLine() { $this->line = $this->line + 1; } /** * @return array Retorna un array con las lineas que produjeron errores. */ public function getErrorLineExecution() { return $this->errorLineExecution; } /** * @param array $errorLineExecution Setea un array con las lineas que produjeron errores. */ public function setErrorLineExecution($errorLineExecution) { $this->errorLineExecution = $errorLineExecution; } /** * Agrega una linea de error. * @param string $type Contiene el tipo de sentencia sql. * @param \Throwable $ex Contiene una excepcion. */ private function addErrorLineExecution($type, \Throwable $ex) { if (!array_key_exists($type, $this->errorLineExecution)) { $this->errorLineExecution[$type] = array(); } array_push($this->errorLineExecution[$type], "Line: " . $this->getLine() . " => [" . $ex->getCode() . "] = " . $ex->getMessage()); } /** * @return array Retorna una array con el valor de la ejecucion de cada linea. */ public function getLineExecution() { return $this->lineExecution; } /** * @param array $lineExecution Setea un array con las lineas de ejecucion. */ public function setLineExecution($lineExecution) { $this->lineExecution = $lineExecution; } /** * Agrega el valor de la ejecucion. * @param string $type Contiene el tipo de sentencia sql. * @param string $value Contiene el valor de la ejecucion. */ private function addLineExecution($type, $value) { if (!array_key_exists($type, $this->lineExecution)) { $this->lineExecution[$type] = array(); } array_push($this->lineExecution[$type], "Line: " . $this->getLine() . " => " . $value); } /** * Realiza un up de la modificaciones DDL. Siempre son agregados. * Para realizar sentencias DML utilizarlos metodos preUp y postUp. * @param Schema $schema Contiene el objeto esquema. */ public function up(Schema $schema) { } /** * Realiza un up de la modificaciones DDL. Siempre son eliminacion. * Para realizar sentencias DML utilizarlos metodos preDown y postDown. * @param Schema $schema Contiene el objeto esquema. */ public function down(Schema $schema) { } /** * Procesa un yaml para generar las sentencias DML que luego seran ejecutadas en la base de datos. * El directorio origen es app/DoctrineMigrations en adelante. * @param string $dir Contiene el nombre del directorio a leer. * @param string $fileName Contiene el nombre del archivo a incorporar. */ public function interpretYaml($dir, $fileName) { $dir = trim($dir); if (!(substr($dir, count($dir) - 2, count($dir) - 2) == "\\" || substr($dir, count($dir) - 2, count($dir) - 2) == "/") ) { $dir = $dir . "/"; } // leo el yaml $value = $this->readYaml($dir, $fileName); if ($value != null && count($value) > 0) { // paso las key a mayusculas foreach ($value as $key => $val) { unset($value[$key]); $value[strtoupper($key)] = $val; } // reemplazo las keys que poseen importkey $value = $this->replaceImportsKey($dir, $value); // reemplazo los valores que poseen import $value = $this->replaceImportsValue($dir, $value); // lo hago de esta forma para que se ejecuten de acuerdo a como se escribe el yaml. $this->setLine(0); foreach ($value as $key => $val) { if (strtoupper($key) === MigrationsBase::INSERT) { $this->createInserts($value[MigrationsBase::INSERT]); } else if (strtoupper($key) === MigrationsBase::UPDATE) { $this->createUpdates($value[MigrationsBase::UPDATE]); } else if (strtoupper($key) === MigrationsBase::DELETE) { $this->createDeletes($value[MigrationsBase::DELETE]); } else { die("Valor no esperado"); } } } } /** * Crea los insert a partir de una estructura yaml. Se puede utilizar la palabra clave "ignore", "replace" o "orupdate". * El "replace" sobreescribe al "ignore" y el "orupdate" sobreescribe al "replace". * @param array $arrayInsert Contiene la estructura yaml para los insert. */ private function createInserts($arrayInsert) { foreach ($arrayInsert as $table => $inserts) { // recorro las tablas foreach ($inserts as $key => $valueKey) { // recorro cada uno de los insert que quiero hacer // almacena los campos $fields = ""; // almacena el valor de los campos $valuesFields = ""; // me dice si tengo que utilizar la palabra ignore $ignore = " "; // contiene la primer palabra de la sentencia (INSERT/REPLACE) $insert = "INSERT"; // me dice si es un insert or update $orUpdate = false; // contiene los valores del insert or update $orUpdateValues = ""; // contiene los valores para el bind del stament $arrayPrepare = array(); foreach ($valueKey as $field => $value) { // recorro los datos a insertar $field = strtolower(trim($field)); $value = trim($value); if (strlen($field) > 0 && strlen($value) > 0) { if ($field === 'ignore') { $value = strtolower($value); if ($value === '1' || $value === 'true') { if ($insert === 'INSERT') { $ignore = " IGNORE "; } } } else if ($field === 'replace') { $value = strtolower($value); if ($value === '1' || $value === 'true') { $insert = "REPLACE"; $ignore = " "; } } else if ($field === 'orupdate') { $value = strtolower($value); if ($value === '1' || $value === 'true') { $orUpdate = true; $ignore = " "; $insert = "INSERT"; } } else { $arrayPrepare[':' . $field] = $value; $fields = $fields . $field . ", "; $valuesFields = $valuesFields . ":" . $field . ", "; $orUpdateValues = $orUpdateValues . $field . " = " . ":" . $field . ", "; } } } if (strlen($fields) > 1) { $fields = substr($fields, 0, strlen($fields) - 2); } if (strlen($valuesFields) > 1) { $valuesFields = substr($valuesFields, 0, strlen($valuesFields) - 2); } if (strlen($orUpdateValues) > 1) { $orUpdateValues = substr($orUpdateValues, 0, strlen($orUpdateValues) - 2); } if (strlen($fields) > 1 && strlen($valuesFields) > 1) { $sql = $insert . $ignore . "INTO " . $table . " (" . $fields . ") VALUES (" . $valuesFields . ")"; if ($orUpdate) { $sql .= " ON DUPLICATE KEY UPDATE " . $orUpdateValues . ";"; } else { $sql .= ";"; } $this->executeSQL($sql, MigrationsBase::INSERT, $arrayPrepare); } } } } /** * Crea los update a partir de una estructura yaml. * @param array $arrayInsert Contiene la estructura yaml para los insert. */ private function createUpdates($arrayInsert) { foreach ($arrayInsert as $table => $inserts) { // recorro las tablas foreach ($inserts as $key => $valueKey) { // recorro cada uno de los insert que quiero hacer $set = ""; $where = ""; // contiene los valores para el bind del stament $arrayPrepare = array(); foreach ($valueKey as $field => $value) { // recorro los datos a realizar un update if (strlen(trim($field)) > 0 && strlen(trim($value)) > 0) { if ($field === "where") { $where = $value; } else { $arrayPrepare[':' . $field] = $value; $set = $set . $field . " = :" . $field . ", "; } } } if (strlen($set) > 1) { $set = substr($set, 0, strlen($set) - 2); } if (strlen($set) > 1) { $sql = "UPDATE " . $table . " SET " . $set . " WHERE " . $where . ";"; $this->executeSQL($sql, MigrationsBase::UPDATE, $arrayPrepare); } } } } /** * Crea los delete a partir de una estructura yaml. * @param array $arrayInsert Contiene la estructura yaml para los insert. */ private function createDeletes($arrayInsert) { foreach ($arrayInsert as $table => $inserts) { // recorro las tablas foreach ($inserts as $key => $valueKey) { // recorro cada uno de los insert que quiero hacer $where = ""; foreach ($valueKey as $field => $value) { // recorro los datos a realizar un update if (strlen(trim($field)) > 0 && strlen(trim($value)) > 0) { if ($field === "where") { $where = $value; } } } $sql = "DELETE FROM " . $table . " WHERE " . $where . ";"; $this->executeSQL($sql, MigrationsBase::DELETE); } } } /** * Obtiene el contenido de un archivo yaml. * @param string $dir Contiene el directorio de trabajo. Por defecto "DoctrineMigrations". * @param string $archivo Contiene el nombre del archivo a incorporar. * @return bool|string Retorna el contenido del archivo. */ private function readYaml($dir, $archivo) { return Yaml::parse(file_get_contents($dir . $archivo)); } /** * Obtiene el contenido de un archivo. * @param string $dir Contiene el directorio de trabajo. Por defecto "DoctrineMigrations". * @param string $archivo Contiene el nombre del archivo a incorporar. * @return bool|string Retorna el contenido del archivo. */ private function readImportInValues($dir, $archivo) { return file_get_contents($dir . $archivo); } /** * Reemplaza el contenido de los imports dentro de los values. * @param string $dir Contiene el directorio de trabajo. Por defecto "DoctrineMigrations". * @param array $valores Contiene el array el contenido del yaml. * @return array Retorna el array con los valores cambiados. */ private function replaceImportsValue($dir, $valores) { try { foreach ($valores as $key => $value) { if (is_array($value)) { if (count($value) == 1 && array_key_exists("import", $value)) { if (file_exists($dir . $value["import"])) { $valores[$key] = $this->readImportInValues($dir, $value["import"]); } else { $valores[$key] = "FILE NOT FOUND"; } } else { $valores[$key] = $this->replaceImportsValue($dir, $value); } } } } catch (\Symfony\Component\Debug\Exception\ContextErrorException $e) { var_dump($e); } return $valores; } /** * Reemplaza el contenido de los imports dentro de las key. * @param string $dir Contiene el directorio de trabajo. Por defecto "DoctrineMigrations". * @param array $valores Contiene el array el contenido del yaml. * @return array Retorna el array con los valores cambiados. */ private function replaceImportsKey($dir, $valores) { try { foreach ($valores as $key => $value) { if (is_array($value)) { $valores[$key] = $this->replaceImportsKey($dir, $value); } else { if (trim($key) === "importkey") { if (file_exists($dir . $value)) { $valores = $this->readYaml($dir, $value); } else { $valores[$key] = "FILE NOT FOUND"; } } } } } catch (\Symfony\Component\Debug\Exception\ContextErrorException $e) { var_dump($e); } return $valores; } /** * Funcion ejecuta el YAML en la base de datos dentro de una transaccion. * @param string $dir Contiene el path del directorio donde se encuentra el archio a leer. * @param string $file Contiene el nombre de archivo a procesar. */ public function executeYaml($dir, $file) { try { $tmp = explode("\\", get_class($this)); if ($tmp != null && count($tmp) > 0) { $version = $tmp[count($tmp) - 1]; } else { $version = ""; } echo "\n\tInicio del procesamiento de la migracion: " . $version; $this->connection->beginTransaction(); try { $this->interpretYaml($dir, $file); if (count($this->getErrorLineExecution()) > 0) { //se produjeron errores echo "\n\t\tSe produjeron errores de sql.\n"; } else { $this->connection->commit(); echo "\n\t\tMigracion correcta.\n"; } } catch (\Throwable $ex) { $this->connection->rollBack(); echo "\n\t\tSe produjeron errores por una excepcion. " . $ex->getMessage() . "\n"; } } catch (\Throwable $e) { echo "\n\t\tSe produjeron errores genericos. " . $e->getMessage() . "\n"; } } /** * Funcion que ejecuta una sentencia sql. * @param string $sql Contiene el sql a ejecutar. * @param string $type Contiene el tipo de sentencia. * @param array $arrayPrepare Contiene un array con los valores. La key contiene el valor a * buscar en la sentencia sql y el value es el valor. */ public function executeSQL($sql, $type, $arrayPrepare = null) { $this->connection->executeUpdate($sql, (array)$arrayPrepare); $this->sumLine(); } /** * Funcion que obtiene el numero de version. * @param mixed $obj Contiene el objeto de ejecucion. * @return string Retorna el numero de version que estoy ejecutando. */ public function getMigrationNumber($obj) { $arr = explode("\\", get_class($obj)); return str_ireplace("version", "", $arr[count($arr) - 1]); } /** * Borra la migracion de la tabla de migraciones. * @param string $obj Contiene el objeto this. */ public function deleteMigrationsVersion($obj) { if ($this->existTable("migration_versions")) { $this->connection->beginTransaction(); $stmt = $this->connection->prepare( "DELETE FROM migration_versions " . "WHERE migration_versions.version = '" . $this->getMigrationNumber($obj) . "'"); $stmt->execute(); $this->connection->commit(); } } /** * Verifica si la migracion ya se incorporo. * @param string $obj Contiene el objeto this. * @return bool Retorna TRUE si ya se realizo la migracion. */ public function verifyMigrationsVersion($obj) { $existe = false; try { if ($this->existTable("migration_versions")) { $nroVersion = $this->getMigrationNumber($obj); $base = $this->getDataBaseName(); $sql = "SELECT ifnull(version, '') as nombre " . "FROM migration_versions " . "WHERE " . $base . ".migration_versions.version = '" . $nroVersion . "';"; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { if (strtolower($value["nombre"]) === strtolower($nroVersion)) { $existe = true; break; } } } return $existe; } catch (\Throwable $e) { return $existe; } } /** * Funcion que muestra por pantalla el resultado de la ejecucion y de los errores. */ public function showResult() { if (count($this->getLineExecution()) > 0) { echo "-----------------------------------------------\n"; echo " EJECUCIONES\n"; echo "-----------------------------------------------\n"; foreach ($this->getLineExecution() as $key => $value) { echo $key . "\n"; foreach ($this->getLineExecution()[$key] as $k => $v) { echo "\t" . $v . "\n"; } } } if (count($this->getErrorLineExecution()) > 0) { echo "-----------------------------------------------\n"; echo " ERRORES\n"; echo "-----------------------------------------------\n"; foreach ($this->getErrorLineExecution() as $key => $value) { echo $key . "\n"; foreach ($this->getErrorLineExecution()[$key] as $k => $v) { echo "\t" . $v . "\n"; } } } } /** * Funcion que verifica si existe un campo de una tabla. * @param string $table Contiene el nombre de la tabla. * @param string $field Contiene el nombre el campo. * @return bool Retorna true si el campo existe. */ public function existFieldInTable($table, $field) { $existe = false; try { $base = $this->getDataBaseName(); $sql = "SELECT ifnull(COLUMN_NAME, '') as nombre " . "FROM information_schema.columns " . "WHERE TABLE_SCHEMA = '" . $base . "' AND " . " TABLE_NAME = '" . $table . "' AND " . " COLUMN_NAME = '" . $field . "'; "; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { if (strtolower($value["nombre"]) === strtolower($field)) { $existe = true; break; } } return $existe; } catch (\Throwable $e) { return $existe; } } /** * Funcion que verifica si existe un valor dentro de una tabla. * @param string $table Contiene el nombre de la tabla. * @param string $field Contiene el/los campos a mostrar. * @param string $where Contiene el where de la consulta. Solo los campos con los valores. SIN * LA PALABRA CLAVE. * @return array Retorna el array con los datos encontrados. O NULL en caso de error. */ public function existValueInTable($table, $field, $where) { try { $sql = "SELECT " . $field . " " . "FROM " . $table . " " . "WHERE " . $where . "; "; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); return $resp; } catch (\Throwable $e) { return null; } } /** * Funcion que verifica si existe un campo de una tabla. * @param string $table Contiene el nombre de la tabla. * @param string $field Contiene el nombre el campo. * @param string $type Contiene el typo de dato. * @return bool Retorna true si el campo existe. */ public function existFieldType($table, $field, $type) { $existe = false; try { $base = $this->getDataBaseName(); $sql = "SELECT ifnull(DATA_TYPE, '') as nombre " . "FROM information_schema.columns " . "WHERE TABLE_SCHEMA = '" . $base . "' AND " . " TABLE_NAME = '" . $table . "' AND " . " COLUMN_NAME = '" . $field . "' " . "LIMIT 1;"; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { if (strtolower($value["nombre"]) === strtolower($type)) { $existe = true; break; } } return $existe; } catch (\Throwable $e) { return $existe; } } /** * Funcion que verifica si existe una tabla. * @param string $table Contiene el nombre de la tabla. * @return bool Retorna true si la tabla existe. */ public function existTable($table) { $existe = false; try { $base = $this->getDataBaseName(); $sql = "SELECT ifnull(TABLE_NAME, '') as nombre " . "FROM information_schema.columns " . "WHERE TABLE_SCHEMA = '" . $base . "' AND " . " TABLE_NAME = '" . $table . "' " . "LIMIT 1;"; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { if (strtolower($value["nombre"]) === strtolower($table)) { $existe = true; break; } } return $existe; } catch (\Throwable $e) { return $existe; } } /** * Funcion que obtiene el nombre de la base de datos a la cual estoy conectado. * @return string Retorna el nombre de la base de datos o NULL en caso de error. */ public function getDataBaseName() { $resp = null; try { $stmt = $this->connection->prepare("SELECT DATABASE() as base;"); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { $resp = $value["base"]; break; } return $resp; } catch (\Throwable $e) { return $resp; } } /** * Funcion que obtiene el valor del auto_increment. * @param string $table Contiene el nombre de la tabla. * @return array Retorna el array con los datos encontrados. O NULL en caso de error. */ public function getAutoIncrementValue($table) { $autoIncrement = 1; try { $base = $this->getDataBaseName(); $sql = "SELECT `AUTO_INCREMENT` " . "FROM INFORMATION_SCHEMA.TABLES " . "WHERE TABLE_SCHEMA = '" . $base . "' AND " . " TABLE_NAME = '" . $table . "';"; $stmt = $this->connection->prepare($sql); $stmt->execute(); $resp = $stmt->fetchAll(); foreach ($resp as $key => $value) { if (isset($value["AUTO_INCREMENT"])) { $autoIncrement = $value["AUTO_INCREMENT"]; } } } catch (\Throwable $e) { } return $autoIncrement; } /** * Funcion que setea el valor del auto_increment. * @param string $table Contiene el nombre de la tabla. * @param int $value Contiene el proximo numero autilizar. */ public function setAutoIncrementValue($table, $value) { try { $base = $this->getDataBaseName(); $stmt = $this->connection->prepare("ALTER TABLE " . $base . "." . $table . " AUTO_INCREMENT=" . $value . ";"); $stmt->execute(); } catch (\Throwable $e) { } } }