From bf93834fd361f175b3ea27a18ea19ba8da1c4e0a Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Thu, 17 Sep 2020 20:17:07 +0300 Subject: [PATCH] refactoring --- application/Espo/Controllers/Admin.php | 8 +- application/Espo/Core/DataManager.php | 220 +++++++++++------- .../Core/Utils/Database/Schema/Schema.php | 128 +++++----- .../Utils/Database/Schema/SchemaProxy.php | 60 +++++ 4 files changed, 272 insertions(+), 144 deletions(-) create mode 100644 application/Espo/Core/Utils/Database/Schema/SchemaProxy.php diff --git a/application/Espo/Controllers/Admin.php b/application/Espo/Controllers/Admin.php index 49e123f711..d323940153 100644 --- a/application/Espo/Controllers/Admin.php +++ b/application/Espo/Controllers/Admin.php @@ -51,16 +51,16 @@ class Admin extends \Espo\Core\Controllers\Base throw new BadRequest(); } - $result = $this->getContainer()->get('dataManager')->rebuild(); + $this->getContainer()->get('dataManager')->rebuild(); - return $result; + return true; } public function postActionClearCache($params) { - $result = $this->getContainer()->get('dataManager')->clearCache(); + $this->getContainer()->get('dataManager')->clearCache(); - return $result; + return true; } public function actionJobs() diff --git a/application/Espo/Core/DataManager.php b/application/Espo/Core/DataManager.php index 07ba052919..55e2861c1b 100644 --- a/application/Espo/Core/DataManager.php +++ b/application/Espo/Core/DataManager.php @@ -29,186 +29,237 @@ namespace Espo\Core; -use Espo\Core\Utils\Util; +use Espo\Core\{ + Exceptions\Error, + ORM\EntityManager, + Utils\Metadata, + Utils\Util, + Utils\Config, + Utils\File\Manager as FileManager, + Utils\Metadata\OrmMetadata, + HookManager, + Utils\Database\Schema\SchemaProxy, +}; + +use PDO; +use Throwable; /** * Clears cache, rebuilds the application. */ class DataManager { - private $container; - protected $config; + protected $entityManager; + protected $fileManager; + protected $metadata; + protected $ormMetadata; + protected $hookManager; + protected $schemaProxy; private $cachePath = 'data/cache'; - public function __construct(Container $container) - { - $this->container = $container; - - $this->config = $container->get('config'); - } - - protected function getContainer() - { - return $this->container; + public function __construct( + EntityManager $entityManager, + Config $config, + FileManager $fileManager, + Metadata $metadata, + OrmMetadata $ormMetadata, + HookManager $hookManager, + SchemaProxy $schemaProxy + ) { + $this->entityManager = $entityManager; + $this->config = $config; + $this->fileManager = $fileManager; + $this->metadata = $metadata; + $this->ormMetadata = $ormMetadata; + $this->hookManager = $hookManager; + $this->schemaProxy = $schemaProxy; } /** - * Rebuild the system with metadata, database and cache clearing - * - * @return bool + * Rebuild the system with metadata, database and cache clearing. */ - public function rebuild($target = null) + public function rebuild(?array $entityList = null) { - $result = $this->clearCache(); + $this->clearCache(); $this->disableHooks(); $this->populateConfigParameters(); - $result &= $this->rebuildMetadata(); + $this->rebuildMetadata(); - $result &= $this->rebuildDatabase($target); + $this->rebuildDatabase($entityList); $this->rebuildScheduledJobs(); $this->enableHooks(); - - return $result; } /** - * Clear a cache - * - * @return bool + * Clear a cache. */ public function clearCache() { - $result = $this->getContainer()->get('fileManager')->removeInDir($this->cachePath); + $result = $this->fileManager->removeInDir($this->cachePath); if ($result != true) { - throw new Exceptions\Error("Error while clearing cache"); + throw new Error("Error while clearing cache"); } $this->updateCacheTimestamp(); - - return $result; } /** - * Rebuild database - * - * @return bool + * Rebuild database. */ - public function rebuildDatabase($target = null) + public function rebuildDatabase(?array $entityList = null) { - $schema = $this->getContainer()->get('schema'); + $schema = $this->schemaProxy; try { - $result = $schema->rebuild($target); - } catch (\Throwable $e) { + $result = $schema->rebuild($entityList); + } + catch (Throwable $e) { $result = false; + $GLOBALS['log']->error('Fault to rebuild database schema. Details: '. $e->getMessage()); } if ($result != true) { - throw new Exceptions\Error("Error while rebuilding database. See log file for details."); + throw new Error("Error while rebuilding database. See log file for details."); } - $config = $this->getContainer()->get('config'); + $config = $this->config; $databaseType = strtolower($schema->getDatabaseHelper()->getDatabaseType()); + if (!$config->get('actualDatabaseType') || $config->get('actualDatabaseType') != $databaseType) { $config->set('actualDatabaseType', $databaseType); } $databaseVersion = $schema->getDatabaseHelper()->getDatabaseVersion(); + if (!$config->get('actualDatabaseVersion') || $config->get('actualDatabaseVersion') != $databaseVersion) { $config->set('actualDatabaseVersion', $databaseVersion); } $this->updateCacheTimestamp(); - - return $result; } /** - * Rebuild metadata - * - * @return bool + * Rebuild metadata. */ public function rebuildMetadata() { - $metadata = $this->getContainer()->get('metadata'); + $metadata = $this->metadata; $metadata->init(true); - $ormData = $this->getContainer()->get('ormMetadata')->getData(true); - $this->getContainer()->get('entityManager')->setMetadata($ormData); + $ormData = $this->ormMetadata->getData(true); + + $this->entityManager->setMetadata($ormData); $this->updateCacheTimestamp(); - return empty($ormData) ? false : true; + if (empty($ormData)) { + throw new Error("Error while rebuilding metadata. See log file for details."); + } } + /** + * Rebuild scheduled jobs. Create system jobs. + */ public function rebuildScheduledJobs() { - $metadata = $this->getContainer()->get('metadata'); - $entityManager = $this->getContainer()->get('entityManager'); + $metadata = $this->metadata; + + $entityManager = $this->entityManager; $jobs = $metadata->get(['entityDefs', 'ScheduledJob', 'jobs'], []); $systemJobNameList = []; foreach ($jobs as $jobName => $defs) { - if ($jobName && !empty($defs['isSystem']) && !empty($defs['scheduling'])) { - $systemJobNameList[] = $jobName; - if (!$entityManager->getRepository('ScheduledJob')->where(array( + if (!$jobName) { + continue; + } + + if (empty($defs['isSystem']) || empty($defs['scheduling'])) { + continue; + } + + $systemJobNameList[] = $jobName; + + $sj = $entityManager + ->getRepository('ScheduledJob') + ->where([ 'job' => $jobName, 'status' => 'Active', - 'scheduling' => $defs['scheduling'] - ))->findOne()) { - $job = $entityManager->getRepository('ScheduledJob')->where([ - 'job' => $jobName - ])->findOne(); - if ($job) { - $entityManager->removeEntity($job); - } - $name = $jobName; - if (!empty($defs['name'])) { - $name = $defs['name']; - } - $job = $entityManager->getEntity('ScheduledJob'); - $job->set([ - 'job' => $jobName, - 'status' => 'Active', - 'scheduling' => $defs['scheduling'], - 'isInternal' => true, - 'name' => $name - ]); - $entityManager->saveEntity($job); - } + 'scheduling' => $defs['scheduling'], + ]) + ->findOne(); + + if ($sj) { + continue; } + + $job = $entityManager + ->getRepository('ScheduledJob') + ->where([ + 'job' => $jobName + ]) + ->findOne(); + + if ($job) { + $entityManager->removeEntity($job); + } + + $name = $jobName; + + if (!empty($defs['name'])) { + $name = $defs['name']; + } + + $job = $entityManager->getEntity('ScheduledJob'); + + $job->set([ + 'job' => $jobName, + 'status' => 'Active', + 'scheduling' => $defs['scheduling'], + 'isInternal' => true, + 'name' => $name, + ]); + + $entityManager->saveEntity($job); } - $internalScheduledJobList = $entityManager->getRepository('ScheduledJob')->where([ - 'isInternal' => true - ])->find(); + $internalScheduledJobList = $entityManager + ->getRepository('ScheduledJob') + ->where([ + 'isInternal' => true, + ]) + ->find(); + foreach ($internalScheduledJobList as $scheduledJob) { $jobName = $scheduledJob->get('job'); + if (!in_array($jobName, $systemJobNameList)) { $entityManager->getRepository('ScheduledJob')->deleteFromDb($scheduledJob->id); } } } + /** + * Update cache timestamp. + */ public function updateCacheTimestamp() { - $this->getContainer()->get('config')->updateCacheTimestamp(); - $this->getContainer()->get('config')->save(); /* correct rebuildDatabase() method when remove this line */ + $this->config->updateCacheTimestamp(); - return true; + /* Fix rebuildDatabase() method when remove this line. */ + $this->config->save(); } protected function populateConfigParameters() @@ -230,7 +281,7 @@ class DataManager return; } - $pdo = $this->getContainer()->get('entityManager')->getPDO(); + $pdo = $this->entityManager->getPDO(); $sql = "SHOW VARIABLES LIKE 'ft_min_word_len'"; @@ -239,7 +290,7 @@ class DataManager $fullTextSearchMinLength = null; - if ($row = $sth->fetch(\PDO::FETCH_ASSOC)) { + if ($row = $sth->fetch(PDO::FETCH_ASSOC)) { if (isset($row['Value'])) { $fullTextSearchMinLength = intval($row['Value']); } @@ -259,16 +310,17 @@ class DataManager } $cryptKey = Util::generateSecretKey(); + $config->set('cryptKey', $cryptKey); } protected function disableHooks() { - $this->getContainer()->get('hookManager')->disable(); + $this->hookManager->disable(); } protected function enableHooks() { - $this->getContainer()->get('hookManager')->enable(); + $this->hookManager->enable(); } } diff --git a/application/Espo/Core/Utils/Database/Schema/Schema.php b/application/Espo/Core/Utils/Database/Schema/Schema.php index 96fb96235e..170d7c2255 100644 --- a/application/Espo/Core/Utils/Database/Schema/Schema.php +++ b/application/Espo/Core/Utils/Database/Schema/Schema.php @@ -29,26 +29,33 @@ namespace Espo\Core\Utils\Database\Schema; -use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\{ + Types\Type, + Schema\SchemaDiff as DBALSchemaDiff, + Schema\Schema as DBALSchema, +}; -use Espo\Core\Utils\Config; -use Espo\Core\Utils\Metadata; -use Espo\Core\Utils\File\Manager as FileManager; -use Espo\Core\ORM\EntityManager; -use Espo\Core\Utils\File\ClassParser; -use Espo\Core\Utils\Metadata\OrmMetadata; -use Espo\Core\Utils\Util; +use Espo\Core\{ + Utils\Config, + Utils\Metadata, + Utils\File\Manager as FileManager, + ORM\EntityManager, + Utils\File\ClassParser, + Utils\Metadata\OrmMetadata, + Utils\Util, + Utils\Database\Helper, + Utils\Database\DBAL\Schema\Comparator, + Utils\Database\Converter as DatabaseConverter, +}; + +use Throwable; class Schema { private $config; - private $metadata; - private $fileManager; - private $entityManager; - private $classParser; private $comparator; @@ -63,8 +70,7 @@ class Schema ]; /** - * Paths of rebuild action folders - * @var array + * Paths of rebuild action folders. */ protected $rebuildActionsPath = [ 'corePath' => 'application/Espo/Core/Utils/Database/Schema/rebuildActions', @@ -73,11 +79,10 @@ class Schema /** * Array of rebuildActions classes in format: - * array( - * 'beforeRebuild' => array(...), - * 'afterRebuild' => array(...), - * ) - * @var array + * [ + * 'beforeRebuild' => [...], + * 'afterRebuild' => [...], + * ] */ protected $rebuildActionClasses = null; @@ -95,12 +100,14 @@ class Schema $this->entityManager = $entityManager; $this->classParser = $classParser; - $this->databaseHelper = new \Espo\Core\Utils\Database\Helper($this->config); + $this->databaseHelper = new Helper($this->config); + + $this->comparator = new Comparator(); - $this->comparator = new \Espo\Core\Utils\Database\DBAL\Schema\Comparator(); $this->initFieldTypes(); - $this->converter = new \Espo\Core\Utils\Database\Converter($this->metadata, $this->fileManager, $this->config); + $this->converter = new DatabaseConverter($this->metadata, $this->fileManager, $this->config); + $this->schemaConverter = new Converter($this->metadata, $this->fileManager, $this, $this->config); $this->ormMetadata = $ormMetadata; @@ -159,34 +166,36 @@ class Schema protected function initFieldTypes() { foreach ($this->fieldTypePaths as $path) { - $typeList = $this->getFileManager()->getFileList($path, false, '\.php$'); - if ($typeList !== false) { - foreach ($typeList as $name) { - $typeName = preg_replace('/Type\.php$/i', '', $name); - $dbalTypeName = strtolower($typeName); - $filePath = Util::concatPath($path, $typeName . 'Type'); - $class = Util::getClassName($filePath); + if ($typeList === false) { + continue; + } - if( ! Type::hasType($dbalTypeName) ) { - Type::addType($dbalTypeName, $class); - } else { - Type::overrideType($dbalTypeName, $class); - } + foreach ($typeList as $name) { + $typeName = preg_replace('/Type\.php$/i', '', $name); + $dbalTypeName = strtolower($typeName); - $dbTypeName = method_exists($class, 'getDbTypeName') ? $class::getDbTypeName() : $dbalTypeName; + $filePath = Util::concatPath($path, $typeName . 'Type'); + $class = Util::getClassName($filePath); - $this->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping($dbTypeName, $dbalTypeName); + if (! Type::hasType($dbalTypeName) ) { + Type::addType($dbalTypeName, $class); + } else { + Type::overrideType($dbalTypeName, $class); } + + $dbTypeName = method_exists($class, 'getDbTypeName') ? $class::getDbTypeName() : $dbalTypeName; + + $this->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping($dbTypeName, $dbalTypeName); } } } /* - * Rebuild database schema + * Rebuild database schema. */ - public function rebuild($entityList = null) + public function rebuild(?array $entityList = null) : bool { if (!$this->getConverter()->process()) { return false; @@ -200,8 +209,10 @@ class Schema try { $this->executeRebuildActions('beforeRebuild'); - } catch (\Throwable $e) { + } + catch (Throwable $e) { $GLOBALS['log']->alert('Rebuild database fault: '. $e); + return false; } @@ -209,20 +220,26 @@ class Schema $result = true; $connection = $this->getConnection(); + foreach ($queries as $sql) { $GLOBALS['log']->info('SCHEMA, Execute Query: '.$sql); + try { $result &= (bool) $connection->executeQuery($sql); - } catch (\Throwable $e) { + } + catch (Throwable $e) { $GLOBALS['log']->alert('Rebuild database fault: '. $e); + $result = false; } } try { $this->executeRebuildActions('afterRebuild'); - } catch (\Throwable $e) { + } + catch (Throwable $e) { $GLOBALS['log']->alert('Rebuild database fault: '. $e); + return false; } @@ -230,7 +247,7 @@ class Schema } /* - * Get current database schema + * Get current database schema. * * @return \Doctrine\DBAL\Schema\Schema */ @@ -240,33 +257,29 @@ class Schema } /* - * Get SQL queries of database schema - * - * @params \Doctrine\DBAL\Schema\Schema $schema + * Get SQL queries of database schema. * * @return array - array of SQL queries */ - public function toSql(\Doctrine\DBAL\Schema\SchemaDiff $schema) //Doctrine\DBAL\Schema\SchemaDiff | \Doctrine\DBAL\Schema\Schema + public function toSql(DBALSchemaDiff $schema) { return $schema->toSaveSql($this->getPlatform()); - //return $schema->toSql($this->getPlatform()); //it can return with DROP TABLE } /* - * Get SQL queries to get from one to another schema + * Get SQL queries to get from one to another schema. * - * @return array - array of SQL queries + * @return array - array of SQL queries. */ - public function getDiffSql(\Doctrine\DBAL\Schema\Schema $fromSchema, \Doctrine\DBAL\Schema\Schema $toSchema) + public function getDiffSql(DBALSchema $fromSchema, DBALSchema $toSchema) { $schemaDiff = $this->getComparator()->compare($fromSchema, $toSchema); - return $this->toSql($schemaDiff); //$schemaDiff->toSql($this->getPlatform()); + return $this->toSql($schemaDiff); } /** - * Init Rebuild Actions, get all classes and create them - * @return void + * Init Rebuild Actions, get all classes and create them. */ protected function initRebuildActions($currentSchema = null, $metadataSchema = null) { @@ -275,11 +288,14 @@ class Schema $rebuildActions = $this->getClassParser()->getData($this->rebuildActionsPath, null, $methods); $classes = []; + foreach ($rebuildActions as $actionName => $actionClass) { $rebuildActionClass = new $actionClass($this->metadata, $this->config, $this->entityManager); + if (isset($currentSchema)) { $rebuildActionClass->setCurrentSchema($currentSchema); } + if (isset($metadataSchema)) { $rebuildActionClass->setMetadataSchema($metadataSchema); } @@ -295,9 +311,9 @@ class Schema } /** - * Execute actions for RebuildAction classes - * @param string $action action name, possible values 'beforeRebuild' | 'afterRebuild' - * @return void + * Execute actions for RebuildAction classes. + * + * @param string $action action name, possible values 'beforeRebuild' | 'afterRebuild'. */ protected function executeRebuildActions($action = 'beforeRebuild') { diff --git a/application/Espo/Core/Utils/Database/Schema/SchemaProxy.php b/application/Espo/Core/Utils/Database/Schema/SchemaProxy.php new file mode 100644 index 0000000000..13b5f2c6ec --- /dev/null +++ b/application/Espo/Core/Utils/Database/Schema/SchemaProxy.php @@ -0,0 +1,60 @@ +container = $container; + } + + protected function getSchema() : Schema + { + return $this->container->get('schema'); + } + + public function rebuild(?array $entityList = null) : bool + { + return $this->getSchema()->rebuild(); + } + + public function getDatabaseHelper() : Helper + { + return $this->getSchema()->getDatabaseHelper(); + } +}