From 49bb6771f6b8abf82aa12b69e2b27481d076c202 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 25 Dec 2022 14:07:06 +0200 Subject: [PATCH] pdo factory and dbal ref --- .../Espo/Core/ORM/EntityManagerFactory.php | 49 ++++---- .../Espo/Core/ORM/PDO/PDOFactoryFactory.php | 55 ++++++++ .../Espo/Core/Upgrades/Actions/Base.php | 9 +- .../Espo/Core/Utils/Database/Converter.php | 41 +----- .../Espo/Core/Utils/Database/Helper.php | 103 +++++++-------- .../Core/Utils/Database/Orm/Converter.php | 119 +++++------------- .../Utils/Database/Orm/RelationManager.php | 54 +++----- .../Database/Schema/BaseRebuildActions.php | 34 ++--- .../Core/Utils/Database/Schema/Converter.php | 31 ++--- .../Core/Utils/Database/Schema/Schema.php | 105 ++++++---------- .../Schema/rebuildActions/FulltextIndex.php | 8 +- .../Espo/Core/Utils/Metadata/Helper.php | 13 +- .../Core/Utils/Metadata/OrmMetadataData.php | 33 ++--- .../Espo/Core/Utils/SystemRequirements.php | 31 ++--- .../Espo/ORM/PDO/DefaultPDOProvider.php | 54 +------- application/Espo/ORM/PDO/MysqlPDOFactory.php | 79 ++++++++++++ application/Espo/ORM/PDO/PDOFactory.php | 38 ++++++ .../Espo/Resources/metadata/app/orm.json | 3 + .../Espo/Tools/FieldManager/FieldManager.php | 37 ++---- install/core/Installer.php | 7 +- .../Espo/Core/Utils/Database/HelperTest.php | 29 +---- .../Core/Utils/Database/VarcharFieldTest.php | 2 +- 22 files changed, 412 insertions(+), 522 deletions(-) create mode 100644 application/Espo/Core/ORM/PDO/PDOFactoryFactory.php create mode 100644 application/Espo/ORM/PDO/MysqlPDOFactory.php create mode 100644 application/Espo/ORM/PDO/PDOFactory.php diff --git a/application/Espo/Core/ORM/EntityManagerFactory.php b/application/Espo/Core/ORM/EntityManagerFactory.php index 66d4df4dce..744b4a42d6 100644 --- a/application/Espo/Core/ORM/EntityManagerFactory.php +++ b/application/Espo/Core/ORM/EntityManagerFactory.php @@ -29,53 +29,43 @@ namespace Espo\Core\ORM; +use Espo\Core\ORM\PDO\PDOFactoryFactory; use Espo\Core\ORM\QueryComposer\QueryComposerFactory; use Espo\Core\Utils\Config; use Espo\Core\InjectableFactory; use Espo\Core\Binding\BindingContainerBuilder; +use Espo\Core\ORM\QueryComposer\Part\FunctionConverterFactory; use Espo\ORM\Metadata; use Espo\ORM\EventDispatcher; use Espo\ORM\DatabaseParams; +use Espo\ORM\PDO\PDOFactory; use Espo\ORM\QueryComposer\QueryComposerFactory as QueryComposerFactoryInterface; use Espo\ORM\Repository\RepositoryFactory as RepositoryFactoryInterface; -use Espo\ORM\EntityFactory as EntityFactoryInteface; -use Espo\ORM\Value\ValueFactoryFactory as ValueFactoryFactoryInteface; -use Espo\ORM\Value\AttributeExtractorFactory as AttributeExtractorFactoryInteface; +use Espo\ORM\EntityFactory as EntityFactoryInterface; +use Espo\ORM\Value\ValueFactoryFactory as ValueFactoryFactoryInterface; +use Espo\ORM\Value\AttributeExtractorFactory as AttributeExtractorFactoryInterface; use Espo\ORM\PDO\PDOProvider; use Espo\ORM\PDO\DefaultPDOProvider; use Espo\ORM\QueryComposer\Part\FunctionConverterFactory as FunctionConverterFactoryInterface; -use Espo\Core\ORM\QueryComposer\Part\FunctionConverterFactory; - use RuntimeException; class EntityManagerFactory { - private Config $config; - private InjectableFactory $injectableFactory; - private MetadataDataProvider $metadataDataProvider; - private EventDispatcher $eventDispatcher; - - /** - * @var array - */ + /** @var array */ private $driverPlatformMap = [ 'pdo_mysql' => 'Mysql', 'mysqli' => 'Mysql', ]; public function __construct( - Config $config, - InjectableFactory $injectableFactory, - MetadataDataProvider $metadataDataProvider, - EventDispatcher $eventDispatcher - ) { - $this->config = $config; - $this->injectableFactory = $injectableFactory; - $this->metadataDataProvider = $metadataDataProvider; - $this->eventDispatcher = $eventDispatcher; - } + private Config $config, + private InjectableFactory $injectableFactory, + private MetadataDataProvider $metadataDataProvider, + private EventDispatcher $eventDispatcher, + private PDOFactoryFactory $pdoFactoryFactory + ) {} public function create(): EntityManager { @@ -84,7 +74,7 @@ class EntityManagerFactory $repositoryFactory = $this->injectableFactory->createWithBinding( RepositoryFactory::class, BindingContainerBuilder::create() - ->bindInstance(EntityFactoryInteface::class, $entityFactory) + ->bindInstance(EntityFactoryInterface::class, $entityFactory) ->build() ); @@ -113,10 +103,13 @@ class EntityManagerFactory ->build() ); + $pdoFactory = $this->pdoFactoryFactory->create($databaseParams->getPlatform() ?? ''); + $pdoProvider = $this->injectableFactory->createWithBinding( DefaultPDOProvider::class, BindingContainerBuilder::create() ->bindInstance(DatabaseParams::class, $databaseParams) + ->bindInstance(PDOFactory::class, $pdoFactory) ->build() ); @@ -125,7 +118,7 @@ class EntityManagerFactory BindingContainerBuilder::create() ->bindInstance(PDOProvider::class, $pdoProvider) ->bindInstance(Metadata::class, $metadata) - ->bindInstance(EntityFactoryInteface::class, $entityFactory) + ->bindInstance(EntityFactoryInterface::class, $entityFactory) ->bindInstance(FunctionConverterFactoryInterface::class, $functionConverterFactory) ->build() ); @@ -135,9 +128,9 @@ class EntityManagerFactory ->bindInstance(Metadata::class, $metadata) ->bindInstance(QueryComposerFactoryInterface::class, $queryComposerFactory) ->bindInstance(RepositoryFactoryInterface::class, $repositoryFactory) - ->bindInstance(EntityFactoryInteface::class, $entityFactory) - ->bindInstance(ValueFactoryFactoryInteface::class, $valueFactoryFactory) - ->bindInstance(AttributeExtractorFactoryInteface::class, $attributeExtractorFactory) + ->bindInstance(EntityFactoryInterface::class, $entityFactory) + ->bindInstance(ValueFactoryFactoryInterface::class, $valueFactoryFactory) + ->bindInstance(AttributeExtractorFactoryInterface::class, $attributeExtractorFactory) ->bindInstance(EventDispatcher::class, $this->eventDispatcher) ->bindInstance(PDOProvider::class, $pdoProvider) ->bindInstance(FunctionConverterFactoryInterface::class, $functionConverterFactory) diff --git a/application/Espo/Core/ORM/PDO/PDOFactoryFactory.php b/application/Espo/Core/ORM/PDO/PDOFactoryFactory.php new file mode 100644 index 0000000000..55f307b0b5 --- /dev/null +++ b/application/Espo/Core/ORM/PDO/PDOFactoryFactory.php @@ -0,0 +1,55 @@ + $className */ + $className = $this->metadata->get(['app', 'orm', 'pdoFactoryClassNameMap', $platform]); + + if (!$className) { + throw new RuntimeException("Could not create PDOFactory."); + } + + return $this->injectableFactory->create($className); + } +} diff --git a/application/Espo/Core/Upgrades/Actions/Base.php b/application/Espo/Core/Upgrades/Actions/Base.php index 2f646864c2..3f1ab04ca1 100644 --- a/application/Espo/Core/Upgrades/Actions/Base.php +++ b/application/Espo/Core/Upgrades/Actions/Base.php @@ -38,12 +38,12 @@ use Espo\Core\Utils\File\Manager as FileManager; use Espo\Core\{ Container, + InjectableFactory, Upgrades\ActionManager, Utils\File\ZipArchive, Utils\Config\ConfigWriter, Utils\Database\Helper as DatabaseHelper, - Utils\Log, -}; + Utils\Log}; use Composer\Semver\Semver; @@ -217,7 +217,10 @@ abstract class Base protected function getDatabaseHelper() { if (!isset($this->databaseHelper)) { - $this->databaseHelper = new DatabaseHelper($this->getConfig()); + /** @var InjectableFactory $injectableFactory */ + $injectableFactory = $this->getContainer()->get('injectableFactory'); + + $this->databaseHelper = $injectableFactory->create(DatabaseHelper::class); } return $this->databaseHelper; diff --git a/application/Espo/Core/Utils/Database/Converter.php b/application/Espo/Core/Utils/Database/Converter.php index 61a1f3cb4b..bc93faca3e 100644 --- a/application/Espo/Core/Utils/Database/Converter.php +++ b/application/Espo/Core/Utils/Database/Converter.php @@ -29,47 +29,18 @@ namespace Espo\Core\Utils\Database; -use Espo\Core\{ - Utils\Metadata, - Utils\File\Manager as FileManager, - Utils\Config, -}; - +/** + * ORM converter wrapper. + */ class Converter { - private $metadata; - - private $fileManager; - - private $config; - - private Orm\Converter $ormConverter; - - public function __construct(Metadata $metadata, FileManager $fileManager, Config $config) - { - $this->metadata = $metadata; - $this->fileManager = $fileManager; - $this->config = $config; - $this->ormConverter = new Orm\Converter($this->metadata, $this->fileManager, $this->config); - } - - protected function getMetadata(): Metadata - { - return $this->metadata; - } - - protected function getOrmConverter(): Orm\Converter - { - return $this->ormConverter; - } + public function __construct(private Orm\Converter $ormConverter) {} /** - * @return array> + * @return array> */ public function process() { - $data = $this->getOrmConverter()->process(); - - return $data; + return $this->ormConverter->process(); } } diff --git a/application/Espo/Core/Utils/Database/Helper.php b/application/Espo/Core/Utils/Database/Helper.php index b82c2d7e90..2de3fc3130 100644 --- a/application/Espo/Core/Utils/Database/Helper.php +++ b/application/Espo/Core/Utils/Database/Helper.php @@ -29,20 +29,15 @@ namespace Espo\Core\Utils\Database; -use Espo\Core\{ - Utils\Config, -}; +use Espo\Core\ORM\PDO\PDOFactoryFactory; +use Espo\Core\Utils\Config; +use Espo\ORM\DatabaseParams; +use Espo\ORM\PDO\Options as PdoOptions; -use Doctrine\DBAL\{ - Connection as DbalConnection, - Platforms\AbstractPlatform as DbalPlatform, -}; - -use Espo\ORM\{ - DatabaseParams, - PDO\DefaultPDOProvider, - PDO\Options as PdoOptions, -}; +use Doctrine\DBAL\Connection as DbalConnection; +use Doctrine\DBAL\Platforms\AbstractPlatform as DbalPlatform; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\VersionAwarePlatformDriver as Driver; use PDO; use ReflectionClass; @@ -50,31 +45,19 @@ use RuntimeException; class Helper { - private ?Config $config; - - private ?DbalConnection $dbalConnection = null; - - private ?PDO $pdoConnection = null; - - /** - * @var array - */ + /** @var array */ private $driverPlatformMap = [ 'pdo_mysql' => 'Mysql', 'mysqli' => 'Mysql', ]; - /** - * @var array - */ + /** @var array */ protected $dbalDrivers = [ 'mysqli' => 'Doctrine\\DBAL\\Driver\\Mysqli\\Driver', 'pdo_mysql' => 'Espo\\Core\\Utils\\Database\\DBAL\\Driver\\PDO\\MySQL\\Driver', ]; - /** - * @var array - */ + /** @var array */ protected $dbalPlatforms = [ 'MariaDb1027Platform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MariaDb1027Platform', 'MySQL57Platform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQL57Platform', @@ -82,13 +65,16 @@ class Helper 'MySQLPlatform' => 'Espo\\Core\\Utils\\Database\\DBAL\\Platforms\\MySQLPlatform', ]; - public function __construct(?Config $config = null) - { - $this->config = $config; - } + private ?DbalConnection $dbalConnection = null; + private ?PDO $pdoConnection = null; + + public function __construct( + private Config $config, + private PDOFactoryFactory $pdoFactoryFactory + ) {} /** - * @throws \Doctrine\DBAL\Exception + * @throws Exception */ public function getDbalConnection(): DbalConnection { @@ -108,6 +94,9 @@ class Helper return $this->pdoConnection; } + /** + * @deprecated + */ public function setDbalConnection(DbalConnection $dbalConnection): void { $this->dbalConnection = $dbalConnection; @@ -119,26 +108,23 @@ class Helper } /** - * @param array $params - * @throws RuntimeException - * @throws \Doctrine\DBAL\Exception + * @param array $params + * @throws Exception */ public function createDbalConnection(array $params = []): DbalConnection { - if (empty($params) && isset($this->config)) { + if (empty($params)) { $params = $this->config->get('database'); } if (empty($params)) { - throw new RuntimeException('Params cannot be empty for Dbal connection.'); + throw new RuntimeException('Database params cannot be empty for DBAL connection.'); } $databaseParams = $this->createDatabaseParams($params); $driver = $this->createDbalDriver($params); - $version = $this->getFullDatabaseVersion() ?? ''; - $platform = $driver->createDatabasePlatformForVersion($version); return new DbalConnection( @@ -158,9 +144,8 @@ class Helper /** * - * @param array $params - * @return \Doctrine\DBAL\VersionAwarePlatformDriver - * @throws RuntimeException + * @param array $params + * @return Driver */ private function createDbalDriver(array $params) { @@ -170,21 +155,19 @@ class Helper throw new RuntimeException('Unknown database driver.'); } - /** @var class-string<\Doctrine\DBAL\VersionAwarePlatformDriver> $driverClass */ + /** @var class-string $driverClass */ $driverClass = $this->dbalDrivers[$driverName]; if (!class_exists($driverClass)) { throw new RuntimeException('Unknown database class.'); } - $driver = new $driverClass(); - - return $driver; + return new $driverClass(); } /** * @param DbalPlatform $platform - * @return \Doctrine\DBAL\Platforms\AbstractPlatform + * @return DbalPlatform */ private function createDbalPlatform(DbalPlatform $platform) { @@ -193,7 +176,7 @@ class Helper $platformClass = $reflect->getShortName(); if (isset($this->dbalPlatforms[$platformClass])) { - /** @var class-string<\Doctrine\DBAL\Platforms\AbstractPlatform> $className */ + /** @var class-string $className */ $className = $this->dbalPlatforms[$platformClass]; return new $className(); @@ -216,12 +199,10 @@ class Helper 'driver' => 'pdo_mysql', ]; - if (isset($this->config) && $this->config instanceof Config) { - $defaultParams = array_merge( - $defaultParams, - $this->config->get('database') - ); - } + $defaultParams = array_merge( + $defaultParams, + $this->config->get('database') + ); $params = array_merge( $defaultParams, @@ -232,15 +213,17 @@ class Helper unset($params['dbname']); } - $pdoProvider = new DefaultPDOProvider( - $this->createDatabaseParams($params) - ); + $databaseParams = $this->createDatabaseParams($params); - return $pdoProvider->get(); + $platform = $databaseParams->getPlatform(); + + $pdoFactory = $this->pdoFactoryFactory->create($platform ?? ''); + + return $pdoFactory->create($databaseParams); } /** - * @param array $params + * @param array $params * @throws RuntimeException */ private function createDatabaseParams(array $params): DatabaseParams diff --git a/application/Espo/Core/Utils/Database/Orm/Converter.php b/application/Espo/Core/Utils/Database/Orm/Converter.php index 75be968b7c..aa3819f1c7 100644 --- a/application/Espo/Core/Utils/Database/Orm/Converter.php +++ b/application/Espo/Core/Utils/Database/Orm/Converter.php @@ -32,45 +32,20 @@ namespace Espo\Core\Utils\Database\Orm; use Espo\Core\Utils\Util; use Espo\ORM\Entity; use Espo\Core\Utils\Database\Schema\Utils as SchemaUtils; - use Espo\Core\Utils\Metadata; -use Espo\Core\Utils\File\Manager as FileManager; use Espo\Core\Utils\Config; - use Espo\Core\Utils\Metadata\Helper as MetadataHelper; use Espo\Core\Utils\Database\Helper as DatabaseHelper; class Converter { - private $metadata; - - private $fileManager; - - private $config; - - private $metadataHelper; - - private $databaseHelper; - - private $relationManager; - - /** - * @var ?array - */ + /** @var ?array */ private $entityDefs = null; - /** - * @var string - */ - protected $defaultFieldType = 'varchar'; + protected string $defaultFieldType = 'varchar'; /** - * @var string - */ - protected $defaultNaming = 'postfix'; - - /** - * @var array + * @var array */ protected $defaultLength = [ 'varchar' => 255, @@ -78,7 +53,7 @@ class Converter ]; /** - * @var array + * @var array */ protected $defaultValue = [ 'bool' => false, @@ -87,7 +62,7 @@ class Converter /** * Mapping entityDefs => ORM. * - * @var array + * @var array */ protected $fieldAccordances = [ 'type' => 'type', @@ -113,7 +88,7 @@ class Converter ]; /** - * @var array + * @var array */ protected $idParams = [ 'dbType' => 'varchar', @@ -130,65 +105,31 @@ class Converter 'additionalTables', ]; - public function __construct(Metadata $metadata, FileManager $fileManager, Config $config) - { - $this->metadata = $metadata; - $this->fileManager = $fileManager; - $this->config = $config; - - $this->relationManager = new RelationManager($this->metadata, $config); - - $this->metadataHelper = new MetadataHelper($this->metadata); - $this->databaseHelper = new DatabaseHelper($this->config); - } - - protected function getMetadata(): Metadata - { - return $this->metadata; - } - - protected function getConfig(): Config - { - return $this->config; - } + public function __construct( + private Metadata $metadata, + private Config $config, + private DatabaseHelper $databaseHelper, + private RelationManager $relationManager, + private MetadataHelper $metadataHelper + ) {} /** * @param bool $reload - * @return array + * @return array */ - protected function getEntityDefs($reload = false) + private function getEntityDefs($reload = false) { if (empty($this->entityDefs) || $reload) { - $this->entityDefs = $this->getMetadata()->get('entityDefs'); + $this->entityDefs = $this->metadata->get('entityDefs'); } return $this->entityDefs; } - protected function getFileManager(): FileManager - { - return $this->fileManager; - } - - protected function getRelationManager(): RelationManager - { - return $this->relationManager; - } - - protected function getMetadataHelper(): MetadataHelper - { - return $this->metadataHelper; - } - - protected function getDatabaseHelper(): DatabaseHelper - { - return $this->databaseHelper; - } - /** * Covert metadata > entityDefs to ORM metadata. * - * @return array> + * @return array> */ public function process(): array { @@ -431,7 +372,7 @@ class Converter continue; } - $fieldTypeMetadata = $this->getMetadataHelper()->getFieldDefsByType($attributeParams); + $fieldTypeMetadata = $this->metadataHelper->getFieldDefsByType($attributeParams); $fieldDefs = $this->convertField($entityType, $attribute, $attributeParams, $fieldTypeMetadata); @@ -447,7 +388,7 @@ class Converter } if (isset($fieldTypeMetadata['linkDefs'])) { - $linkDefs = $this->getMetadataHelper()->getLinkDefsInFieldMeta( + $linkDefs = $this->metadataHelper->getLinkDefsInFieldMeta( $entityType, $attributeParams ); @@ -538,7 +479,7 @@ class Converter } // @todo move to separate file - $scopeDefs = $this->getMetadata()->get('scopes.'.$entityType); + $scopeDefs = $this->metadata->get('scopes.'.$entityType); if (isset($scopeDefs['stream']) && $scopeDefs['stream']) { if (!isset($entityMetadata['fields']['isFollowed'])) { @@ -585,7 +526,7 @@ class Converter ?array $fieldTypeMetadata = null ) { if (!isset($fieldTypeMetadata)) { - $fieldTypeMetadata = $this->getMetadataHelper()->getFieldDefsByType($fieldParams); + $fieldTypeMetadata = $this->metadataHelper->getFieldDefsByType($fieldParams); } $this->prepareFieldParamsBeforeConvert($fieldParams); @@ -658,7 +599,7 @@ class Converter continue; } - $convertedLink = $this->getRelationManager()->convert($linkName, $linkParams, $entityType, $ormMetadata); + $convertedLink = $this->relationManager->convert($linkName, $linkParams, $entityType, $ormMetadata); if (isset($convertedLink)) { /** @var array $relationships */ @@ -713,21 +654,21 @@ class Converter */ protected function applyFullTextSearch(array &$ormMetadata, string $entityType) { - if (!$this->getDatabaseHelper()->doesTableSupportFulltext(Util::toUnderScore($entityType))) { + if (!$this->databaseHelper->doesTableSupportFulltext(Util::toUnderScore($entityType))) { return; } - if (!$this->getMetadata()->get(['entityDefs', $entityType, 'collection', 'fullTextSearch'])) { + if (!$this->metadata->get(['entityDefs', $entityType, 'collection', 'fullTextSearch'])) { return; } - $fieldList = $this->getMetadata() + $fieldList = $this->metadata ->get(['entityDefs', $entityType, 'collection', 'textFilterFields'], ['name']); $fullTextSearchColumnList = []; foreach ($fieldList as $field) { - $defs = $this->getMetadata()->get(['entityDefs', $entityType, 'fields', $field], []); + $defs = $this->metadata->get(['entityDefs', $entityType, 'fields', $field], []); if (empty($defs['type'])) { continue; @@ -739,14 +680,14 @@ class Converter continue; } - if (!$this->getMetadata()->get(['fields', $fieldType, 'fullTextSearch'])) { + if (!$this->metadata->get(['fields', $fieldType, 'fullTextSearch'])) { continue; } - $partList = $this->getMetadata()->get(['fields', $fieldType, 'fullTextSearchColumnList']); + $partList = $this->metadata->get(['fields', $fieldType, 'fullTextSearchColumnList']); if ($partList) { - if ($this->getMetadata()->get(['fields', $fieldType, 'naming']) === 'prefix') { + if ($this->metadata->get(['fields', $fieldType, 'naming']) === 'prefix') { foreach ($partList as $part) { $fullTextSearchColumnList[] = $part . ucfirst($field); } @@ -777,7 +718,7 @@ class Converter } /** - * @param array $ormMetadata + * @param array $ormMetadata * @param string $entityType * @return void */ diff --git a/application/Espo/Core/Utils/Database/Orm/RelationManager.php b/application/Espo/Core/Utils/Database/Orm/RelationManager.php index 7dcf2a6b8c..5400fb56e4 100644 --- a/application/Espo/Core/Utils/Database/Orm/RelationManager.php +++ b/application/Espo/Core/Utils/Database/Orm/RelationManager.php @@ -35,29 +35,19 @@ use Espo\Core\Utils\Config; class RelationManager { - private Metadata $metadata; - - private Config $config; - - public function __construct(Metadata $metadata, Config $config) - { - $this->metadata = $metadata; - $this->config = $config; - } - - protected function getMetadata(): Metadata - { - return $this->metadata; - } + public function __construct( + private Metadata $metadata, + private Config $config + ) {} /** * @param string $entityName - * @param array $linkParams + * @param array $linkParams * @return string */ public function getLinkEntityName($entityName, $linkParams) { - return isset($linkParams['entity']) ? $linkParams['entity'] : $entityName; + return $linkParams['entity'] ?? $entityName; } /** @@ -76,14 +66,14 @@ class RelationManager * @param string $relationName * @return class-string<\Espo\Core\Utils\Database\Orm\Relations\Base>|false */ - protected function getRelationClass($relationName) + private function getRelationClass($relationName) { $relationName = ucfirst($relationName); - $className = 'Espo\Custom\Core\Utils\Database\Orm\Relations\\'.$relationName; + $className = 'Espo\Custom\Core\Utils\Database\Orm\Relations\\' . $relationName; if (!class_exists($className)) { - $className = 'Espo\Core\Utils\Database\Orm\Relations\\'.$relationName; + $className = 'Espo\Core\Utils\Database\Orm\Relations\\' . $relationName; } if (class_exists($className)) { @@ -94,20 +84,6 @@ class RelationManager return false; } - /** - * @param string $relationName - */ - protected function methodExists($relationName): bool - { - $className = $this->getRelationClass($relationName); - - if ($className === false) { - return false; - } - - return method_exists($className, 'load'); - } - /** * Get foreign link. * @@ -130,14 +106,14 @@ class RelationManager /** * @param string $linkName - * @param array $linkParams + * @param array $linkParams * @param string $entityName - * @param array $ormMetadata - * @return ?array + * @param array $ormMetadata + * @return ?array */ public function convert($linkName, $linkParams, $entityName, $ormMetadata) { - $entityDefs = $this->getMetadata()->get('entityDefs'); + $entityDefs = $this->metadata->get('entityDefs'); $foreignEntityName = $this->getLinkEntityName($entityName, $linkParams); $foreignLink = $this->getForeignLink($linkParams, $entityDefs[$foreignEntityName]); @@ -152,7 +128,9 @@ class RelationManager $relType = Util::toCamelCase($relType); - $relationName = $this->relationExists($relType) ? $relType /*hasManyHasMany*/ : $currentType /*hasMany*/; + $relationName = $this->relationExists($relType) ? + $relType /*hasManyHasMany*/ : + $currentType /*hasMany*/; // relationDefs defined in separate file if (isset($linkParams['relationName'])) { diff --git a/application/Espo/Core/Utils/Database/Schema/BaseRebuildActions.php b/application/Espo/Core/Utils/Database/Schema/BaseRebuildActions.php index 8892874f14..46f67cd5e3 100644 --- a/application/Espo/Core/Utils/Database/Schema/BaseRebuildActions.php +++ b/application/Espo/Core/Utils/Database/Schema/BaseRebuildActions.php @@ -29,35 +29,25 @@ namespace Espo\Core\Utils\Database\Schema; -use \Doctrine\DBAL\Schema\Schema as DbalSchema; +use Doctrine\DBAL\Schema\Schema as DbalSchema; -use Espo\Core\{ - Utils\Metadata, - Utils\Config, - ORM\EntityManager, - Utils\Log, -}; +use Espo\Core\ORM\EntityManager; +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Log; +use Espo\Core\Utils\Metadata; abstract class BaseRebuildActions { - protected Metadata $metadata; - - protected Config $config; - - protected EntityManager $entityManager; - - protected Log $log; - - /** - * @var ?DbalSchema - */ + /** @var ?DbalSchema */ protected $currentSchema = null; - - /** - * @var ?DbalSchema - */ + /** @var ?DbalSchema */ protected $metadataSchema = null; + protected Metadata $metadata; + protected Config $config; + protected EntityManager $entityManager; + protected Log $log; + public function __construct( Metadata $metadata, Config $config, diff --git a/application/Espo/Core/Utils/Database/Schema/Converter.php b/application/Espo/Core/Utils/Database/Schema/Converter.php index 29f2d26ff5..c9f94baeac 100644 --- a/application/Espo/Core/Utils/Database/Schema/Converter.php +++ b/application/Espo/Core/Utils/Database/Schema/Converter.php @@ -29,39 +29,26 @@ namespace Espo\Core\Utils\Database\Schema; -use Espo\Core\Utils\{ - Util, - Config, - Metadata, - File\Manager as FileManager, - Log, - Database\Schema\Schema, - Database\Schema\Utils as SchemaUtils, - Module\PathProvider, -}; - +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Database\Schema\Utils as SchemaUtils; +use Espo\Core\Utils\File\Manager as FileManager; +use Espo\Core\Utils\Log; +use Espo\Core\Utils\Metadata; +use Espo\Core\Utils\Module\PathProvider; +use Espo\Core\Utils\Util; use Doctrine\DBAL\Schema\Table; - -use Doctrine\DBAL\{ - Schema\Schema as DbalSchema, - Types\Type as DbalType, -}; +use Doctrine\DBAL\Schema\Schema as DbalSchema; +use Doctrine\DBAL\Types\Type as DbalType; class Converter { private ?DbalSchema $dbalSchema = null; - private Schema $databaseSchema; - private FileManager $fileManager; - private Metadata $metadata; - private Config $config; - private Log $log; - private PathProvider $pathProvider; private string $tablesPath = 'Core/Utils/Database/Schema/tables'; diff --git a/application/Espo/Core/Utils/Database/Schema/Schema.php b/application/Espo/Core/Utils/Database/Schema/Schema.php index 24f8130639..718c18ba2e 100644 --- a/application/Espo/Core/Utils/Database/Schema/Schema.php +++ b/application/Espo/Core/Utils/Database/Schema/Schema.php @@ -29,91 +29,63 @@ namespace Espo\Core\Utils\Database\Schema; -use Doctrine\DBAL\{ - Types\Type, - Schema\SchemaDiff as DBALSchemaDiff, - Schema\Schema as DBALSchema, - Connection, - Platforms\AbstractPlatform, -}; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Schema as DBALSchema; +use Doctrine\DBAL\Schema\SchemaDiff as DBALSchemaDiff; +use Doctrine\DBAL\Types\Type; -use Espo\Core\{ - Utils\Config, - Utils\Metadata, - Utils\File\Manager as FileManager, - ORM\EntityManager, - Utils\File\ClassMap, - Utils\Metadata\OrmMetadataData, - Utils\Util, - Utils\Database\Helper, - Utils\Database\DBAL\Schema\Comparator, - Utils\Database\Converter as DatabaseConverter, - Utils\Log, - Utils\Module\PathProvider, -}; +use Espo\Core\InjectableFactory; +use Espo\Core\ORM\EntityManager; +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Database\Converter as DatabaseConverter; +use Espo\Core\Utils\Database\DBAL\Schema\Comparator; +use Espo\Core\Utils\Database\Helper; +use Espo\Core\Utils\File\ClassMap; +use Espo\Core\Utils\File\Manager as FileManager; +use Espo\Core\Utils\Log; +use Espo\Core\Utils\Metadata; +use Espo\Core\Utils\Metadata\OrmMetadataData; +use Espo\Core\Utils\Module\PathProvider; +use Espo\Core\Utils\Util; use Throwable; class Schema { - private Config $config; - - private Metadata $metadata; - - private FileManager $fileManager; - - private EntityManager $entityManager; - - private ClassMap $classMap; - - private Comparator $comparator; - - private DatabaseConverter $converter; - - private Helper $databaseHelper; - - protected OrmMetadataData $ormMetadataData; - - private Log $log; - private string $fieldTypePath = 'application/Espo/Core/Utils/Database/DBAL/FieldTypes'; - private string $rebuildActionsPath = 'Core/Utils/Database/Schema/rebuildActions'; + private Comparator $comparator; + private DatabaseConverter $converter; private Converter $schemaConverter; /** * @var ?array{ - * beforeRebuild: \Espo\Core\Utils\Database\Schema\BaseRebuildActions[], - * afterRebuild: \Espo\Core\Utils\Database\Schema\BaseRebuildActions[], + * beforeRebuild: BaseRebuildActions[], + * afterRebuild: BaseRebuildActions[], * } */ protected $rebuildActions = null; public function __construct( - Config $config, - Metadata $metadata, - FileManager $fileManager, - EntityManager $entityManager, - ClassMap $classMap, - OrmMetadataData $ormMetadataData, - Log $log, - PathProvider $pathProvider + private Config $config, + private Metadata $metadata, + private FileManager $fileManager, + private ClassMap $classMap, + protected OrmMetadataData $ormMetadataData, + private Log $log, + PathProvider $pathProvider, + DatabaseConverter $databaseConverter, + private Helper $databaseHelper, + private InjectableFactory $injectableFactory ) { - $this->config = $config; - $this->metadata = $metadata; - $this->fileManager = $fileManager; - $this->entityManager = $entityManager; - $this->classMap = $classMap; - $this->log = $log; + $this->converter = $databaseConverter; - $this->databaseHelper = new Helper($this->config); $this->comparator = new Comparator(); $this->initFieldTypes(); - $this->converter = new DatabaseConverter($this->metadata, $this->fileManager, $this->config); - $this->schemaConverter = new Converter( $this->metadata, $this->fileManager, @@ -122,8 +94,6 @@ class Schema $this->log, $pathProvider ); - - $this->ormMetadataData = $ormMetadataData; } public function getDatabaseHelper(): Helper @@ -279,7 +249,7 @@ class Schema 'afterRebuild', ]; - /** @var array> $classes */ + /** @var array> $classes */ $classes = $this->classMap->getData($this->rebuildActionsPath, null, $methodList); $objects = [ @@ -288,12 +258,7 @@ class Schema ]; foreach ($classes as $className) { - $actionObj = new $className( - $this->metadata, - $this->config, - $this->entityManager, - $this->log - ); + $actionObj = $this->injectableFactory->create($className); if (isset($currentSchema)) { $actionObj->setCurrentSchema($currentSchema); diff --git a/application/Espo/Core/Utils/Database/Schema/rebuildActions/FulltextIndex.php b/application/Espo/Core/Utils/Database/Schema/rebuildActions/FulltextIndex.php index a2f9676653..7570602104 100644 --- a/application/Espo/Core/Utils/Database/Schema/rebuildActions/FulltextIndex.php +++ b/application/Espo/Core/Utils/Database/Schema/rebuildActions/FulltextIndex.php @@ -35,8 +35,12 @@ use Espo\Core\Utils\Database\Schema\BaseRebuildActions; use Exception; use RuntimeException; -class FulltextIndex extends BaseRebuildActions +use Espo\Core\Di; + +class FulltextIndex extends BaseRebuildActions implements Di\InjectableFactoryAware { + use Di\InjectableFactorySetter; + /** * @return void * @throws \Doctrine\DBAL\Exception @@ -55,7 +59,7 @@ class FulltextIndex extends BaseRebuildActions return; } - $databaseHelper = new Helper($this->getConfig()); + $databaseHelper = $this->injectableFactory->create(Helper::class); $connection = $databaseHelper->getDbalConnection(); diff --git a/application/Espo/Core/Utils/Metadata/Helper.php b/application/Espo/Core/Utils/Metadata/Helper.php index d2654a54ae..525e4834cd 100644 --- a/application/Espo/Core/Utils/Metadata/Helper.php +++ b/application/Espo/Core/Utils/Metadata/Helper.php @@ -32,10 +32,11 @@ namespace Espo\Core\Utils\Metadata; use Espo\Core\Utils\Util; use Espo\Core\Utils\Metadata; +/** + * Warning: Instantiated explicitly. + */ class Helper { - private Metadata $metadata; - protected string $defaultNaming = 'postfix'; /** @@ -43,7 +44,7 @@ class Helper * * @var string[] */ - protected $copiedDefParams = [ + private $copiedDefParams = [ 'readOnly', 'disabled', 'notStorable', @@ -58,10 +59,8 @@ class Helper 'exportDisabled', ]; - public function __construct(Metadata $metadata) - { - $this->metadata = $metadata; - } + public function __construct(private Metadata $metadata) + {} /** * Get field definition by type in metadata, "fields" key. diff --git a/application/Espo/Core/Utils/Metadata/OrmMetadataData.php b/application/Espo/Core/Utils/Metadata/OrmMetadataData.php index 77bf6e2abe..03235693c6 100644 --- a/application/Espo/Core/Utils/Metadata/OrmMetadataData.php +++ b/application/Espo/Core/Utils/Metadata/OrmMetadataData.php @@ -29,41 +29,34 @@ namespace Espo\Core\Utils\Metadata; -use Espo\Core\{ - Utils\Util, - Utils\Metadata, - Utils\File\Manager as FileManager, - Utils\Config, - Utils\Database\Converter, - Utils\DataCache, -}; +use Espo\Core\InjectableFactory; +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Database\Converter; +use Espo\Core\Utils\DataCache; +use Espo\Core\Utils\File\Manager as FileManager; +use Espo\Core\Utils\Metadata; +use Espo\Core\Utils\Util; class OrmMetadataData { - /** - * @var ?array> - */ + /** @var ?array> */ protected $data = null; protected string $cacheKey = 'ormMetadata'; protected bool $useCache; - protected Metadata $metadata; - protected FileManager $fileManager; - protected DataCache $dataCache; - protected Config $config; - private ?Converter $converter = null; public function __construct( Metadata $metadata, FileManager $fileManager, DataCache $dataCache, - Config $config + Config $config, + private InjectableFactory $injectableFactory ) { $this->metadata = $metadata; $this->fileManager = $fileManager; @@ -76,14 +69,14 @@ class OrmMetadataData protected function getConverter(): Converter { if (!isset($this->converter)) { - $this->converter = new Converter($this->metadata, $this->fileManager, $this->config); + $this->converter = $this->injectableFactory->create(Converter::class); } return $this->converter; } /** - * @return array> + * @return array> */ public function getData(bool $reload = false): array { @@ -92,7 +85,7 @@ class OrmMetadataData } if ($this->useCache && $this->dataCache->has($this->cacheKey) && !$reload) { - /** @var array> $data */ + /** @var array> $data */ $data = $this->dataCache->get($this->cacheKey); $this->data = $data; diff --git a/application/Espo/Core/Utils/SystemRequirements.php b/application/Espo/Core/Utils/SystemRequirements.php index a297f6b78c..6302db1ef0 100644 --- a/application/Espo/Core/Utils/SystemRequirements.php +++ b/application/Espo/Core/Utils/SystemRequirements.php @@ -29,36 +29,19 @@ namespace Espo\Core\Utils; -use Espo\Core\{ - Utils\Config, - Utils\File\Manager as FileManager, - Utils\System, - Utils\Database\Helper as DatabaseHelper, -}; +use Espo\Core\Utils\Database\Helper as DatabaseHelper; +use Espo\Core\Utils\File\Manager as FileManager; use PDO; class SystemRequirements { - private Config $config; - - private FileManager $fileManager; - - private System $systemHelper; - - private DatabaseHelper $databaseHelper; - public function __construct( - Config $config, - FileManager $fileManager, - System $systemHelper, - DatabaseHelper $databaseHelper - ) { - $this->config = $config; - $this->fileManager = $fileManager; - $this->systemHelper = $systemHelper; - $this->databaseHelper = $databaseHelper; - } + private Config $config, + private FileManager $fileManager, + private System $systemHelper, + private DatabaseHelper $databaseHelper + ) {} /** * @return array{ diff --git a/application/Espo/ORM/PDO/DefaultPDOProvider.php b/application/Espo/ORM/PDO/DefaultPDOProvider.php index 977743ed72..2c6cdee631 100644 --- a/application/Espo/ORM/PDO/DefaultPDOProvider.php +++ b/application/Espo/ORM/PDO/DefaultPDOProvider.php @@ -30,23 +30,16 @@ namespace Espo\ORM\PDO; use Espo\ORM\DatabaseParams; - use PDO; -use RuntimeException; class DefaultPDOProvider implements PDOProvider { - private $databaseParams; + private ?PDO $pdo = null; - /** - * @var ?PDO - */ - private $pdo = null; - - public function __construct(DatabaseParams $databaseParams) - { - $this->databaseParams = $databaseParams; - } + public function __construct( + private DatabaseParams $databaseParams, + private PDOFactory $pdoFactory + ) {} public function get(): PDO { @@ -61,41 +54,6 @@ class DefaultPDOProvider implements PDOProvider private function intPDO(): void { - $platform = strtolower($this->databaseParams->getPlatform() ?? ''); - - $host = $this->databaseParams->getHost(); - $port = $this->databaseParams->getPort(); - $dbname = $this->databaseParams->getName(); - $charset = $this->databaseParams->getCharset(); - $username = $this->databaseParams->getUsername(); - $password = $this->databaseParams->getPassword(); - - if (!$platform) { - throw new RuntimeException("No 'platform' parameter."); - } - - if (!$host) { - throw new RuntimeException("No 'host' parameter."); - } - - $dsn = $platform . ':' . 'host=' . $host; - - if ($port) { - $dsn .= ';' . 'port=' . (string) $port; - } - - if ($dbname) { - $dsn .= ';' . 'dbname=' . $dbname; - } - - if ($charset) { - $dsn .= ';' . 'charset=' . $charset; - } - - $options = Options::getOptionsFromDatabaseParams($this->databaseParams); - - $this->pdo = new PDO($dsn, $username, $password, $options); - - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->pdo = $this->pdoFactory->create($this->databaseParams); } } diff --git a/application/Espo/ORM/PDO/MysqlPDOFactory.php b/application/Espo/ORM/PDO/MysqlPDOFactory.php new file mode 100644 index 0000000000..cdeccd43ea --- /dev/null +++ b/application/Espo/ORM/PDO/MysqlPDOFactory.php @@ -0,0 +1,79 @@ +getPlatform() ?? ''); + + $host = $databaseParams->getHost(); + $port = $databaseParams->getPort(); + $dbname = $databaseParams->getName(); + $charset = $databaseParams->getCharset(); + $username = $databaseParams->getUsername(); + $password = $databaseParams->getPassword(); + + if (!$platform) { + throw new RuntimeException("No 'platform' parameter."); + } + + if (!$host) { + throw new RuntimeException("No 'host' parameter."); + } + + $dsn = $platform . ':' . 'host=' . $host; + + if ($port) { + $dsn .= ';' . 'port=' . (string) $port; + } + + if ($dbname) { + $dsn .= ';' . 'dbname=' . $dbname; + } + + if ($charset) { + $dsn .= ';' . 'charset=' . $charset; + } + + $options = Options::getOptionsFromDatabaseParams($databaseParams); + + $pdo = new PDO($dsn, $username, $password, $options); + + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + return $pdo; + } +} diff --git a/application/Espo/ORM/PDO/PDOFactory.php b/application/Espo/ORM/PDO/PDOFactory.php new file mode 100644 index 0000000000..3f1ff94d76 --- /dev/null +++ b/application/Espo/ORM/PDO/PDOFactory.php @@ -0,0 +1,38 @@ +injectableFactory = $injectableFactory; - $this->metadata = $metadata; - $this->language = $language; - $this->baseLanguage = $baseLanguage; - - $this->metadataHelper = new MetadataHelper($this->metadata); - } + private InjectableFactory $injectableFactory, + private Metadata $metadata, + private Language $language, + private Language $baseLanguage, + private MetadataHelper $metadataHelper + ) {} /** * @return array diff --git a/install/core/Installer.php b/install/core/Installer.php index 160b5e08a5..08d1dd3d27 100644 --- a/install/core/Installer.php +++ b/install/core/Installer.php @@ -91,7 +91,7 @@ class Installer $this->systemHelper = new SystemHelper(); - $this->databaseHelper = new DatabaseHelper($this->getConfig()); + $this->databaseHelper = $this->getInjectableFactory()->create(DatabaseHelper::class); } private function initialize(): void @@ -162,6 +162,11 @@ class Installer return $this->app->getContainer()->get('metadata'); } + public function getInjectableFactory(): InjectableFactory + { + return $this->app->getContainer()->get('injectableFactory'); + } + public function getConfig(): Config { return $this->app->getContainer()->get('config'); diff --git a/tests/integration/Espo/Core/Utils/Database/HelperTest.php b/tests/integration/Espo/Core/Utils/Database/HelperTest.php index 54070f5317..7c84b789e0 100644 --- a/tests/integration/Espo/Core/Utils/Database/HelperTest.php +++ b/tests/integration/Espo/Core/Utils/Database/HelperTest.php @@ -30,26 +30,21 @@ namespace tests\integration\Espo\Core\Utils\Database; use tests\unit\ReflectionHelper; - use Espo\Core\Utils\Database\Helper; - use Doctrine\DBAL\Connection; - use Espo\Core\Exceptions\Error; - use PDO; use RuntimeException; class HelperTest extends \tests\integration\Core\BaseTestCase { + protected $helper; protected $reflection; - protected function initTest(bool $noConfig = false) + protected function initTest() { - $config = $noConfig ? null : $this->getContainer()->get('config'); - - $this->helper = new Helper($config); + $this->helper = $this->getInjectableFactory()->create(Helper::class); $this->reflection = new ReflectionHelper($this->helper); } @@ -78,15 +73,6 @@ class HelperTest extends \tests\integration\Core\BaseTestCase ]; } - public function testGetDbalConnectionWithNoConfig() - { - $this->initTest(true); - - $this->expectException(RuntimeException::class); - - $this->helper->getDbalConnection(); - } - public function testGetDbalConnectionWithConfig() { $this->initTest(); @@ -94,15 +80,6 @@ class HelperTest extends \tests\integration\Core\BaseTestCase $this->assertInstanceOf(Connection::class, $this->helper->getDbalConnection()); } - public function testGetPdoConnection() - { - $this->initTest(true); - - $this->expectException(RuntimeException::class); - - $this->helper->getPdoConnection(); - } - public function testGetPdoConnectionWithConfig() { $this->initTest(); diff --git a/tests/integration/Espo/Core/Utils/Database/VarcharFieldTest.php b/tests/integration/Espo/Core/Utils/Database/VarcharFieldTest.php index bb738b1722..e491e7eaf9 100644 --- a/tests/integration/Espo/Core/Utils/Database/VarcharFieldTest.php +++ b/tests/integration/Espo/Core/Utils/Database/VarcharFieldTest.php @@ -199,7 +199,7 @@ class VarcharFieldTest extends Base $this->assertEquals('100', $column['CHARACTER_MAXIMUM_LENGTH']); $this->assertEquals('utf8mb4_unicode_ci', $column['COLLATION_NAME']); - $dbHelper = new DatabaseHelper($this->getContainer()->get('config')); + $dbHelper = $this->getInjectableFactory()->create(DatabaseHelper::class); if ( $dbHelper->getDatabaseType() == 'MariaDB'