pdo factory and dbal ref

This commit is contained in:
Yuri Kuznetsov
2022-12-25 14:07:06 +02:00
parent 28d8fcd31e
commit 49bb6771f6
22 changed files with 412 additions and 522 deletions

View File

@@ -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<string,string>
*/
/** @var array<string, string> */
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)

View File

@@ -0,0 +1,55 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\ORM\PDO;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
use Espo\ORM\PDO\PDOFactory;
use RuntimeException;
class PDOFactoryFactory
{
public function __construct(
private Metadata $metadata,
private InjectableFactory $injectableFactory
) {}
public function create(string $platform): PDOFactory
{
/** @var ?class-string<PDOFactory> $className */
$className = $this->metadata->get(['app', 'orm', 'pdoFactoryClassNameMap', $platform]);
if (!$className) {
throw new RuntimeException("Could not create PDOFactory.");
}
return $this->injectableFactory->create($className);
}
}

View File

@@ -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;

View File

@@ -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<string,array<string,mixed>>
* @return array<string, array<string, mixed>>
*/
public function process()
{
$data = $this->getOrmConverter()->process();
return $data;
return $this->ormConverter->process();
}
}

View File

@@ -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<string,mixed>
*/
/** @var array<string, mixed> */
private $driverPlatformMap = [
'pdo_mysql' => 'Mysql',
'mysqli' => 'Mysql',
];
/**
* @var array<string,mixed>
*/
/** @var array<string, mixed> */
protected $dbalDrivers = [
'mysqli' => 'Doctrine\\DBAL\\Driver\\Mysqli\\Driver',
'pdo_mysql' => 'Espo\\Core\\Utils\\Database\\DBAL\\Driver\\PDO\\MySQL\\Driver',
];
/**
* @var array<string,string>
*/
/** @var array<string, string> */
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<string,mixed> $params
* @throws RuntimeException
* @throws \Doctrine\DBAL\Exception
* @param array<string, mixed> $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<string,mixed> $params
* @return \Doctrine\DBAL\VersionAwarePlatformDriver
* @throws RuntimeException
* @param array<string, mixed> $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<Driver> $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<DbalPlatform> $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<string,mixed> $params
* @param array<string, mixed> $params
* @throws RuntimeException
*/
private function createDatabaseParams(array $params): DatabaseParams

View File

@@ -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<string,mixed>
*/
/** @var ?array<string, mixed> */
private $entityDefs = null;
/**
* @var string
*/
protected $defaultFieldType = 'varchar';
protected string $defaultFieldType = 'varchar';
/**
* @var string
*/
protected $defaultNaming = 'postfix';
/**
* @var array<string,int>
* @var array<string, int>
*/
protected $defaultLength = [
'varchar' => 255,
@@ -78,7 +53,7 @@ class Converter
];
/**
* @var array<string,mixed>
* @var array<string, mixed>
*/
protected $defaultValue = [
'bool' => false,
@@ -87,7 +62,7 @@ class Converter
/**
* Mapping entityDefs => ORM.
*
* @var array<string,string>
* @var array<string, string>
*/
protected $fieldAccordances = [
'type' => 'type',
@@ -113,7 +88,7 @@ class Converter
];
/**
* @var array<string,mixed>
* @var array<string, mixed>
*/
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<string,mixed>
* @return array<string, mixed>
*/
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<string,array<string,mixed>>
* @return array<string, array<string, mixed>>
*/
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<string,mixed> $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<string,mixed> $ormMetadata
* @param array<string, mixed> $ormMetadata
* @param string $entityType
* @return void
*/

View File

@@ -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<string,mixed> $linkParams
* @param array<string, mixed> $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<string,mixed> $linkParams
* @param array<string, mixed> $linkParams
* @param string $entityName
* @param array<string,mixed> $ormMetadata
* @return ?array<string,mixed>
* @param array<string, mixed> $ormMetadata
* @return ?array<string, mixed>
*/
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'])) {

View File

@@ -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,

View File

@@ -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';

View File

@@ -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<string,class-string<\Espo\Core\Utils\Database\Schema\BaseRebuildActions>> $classes */
/** @var array<string, class-string<BaseRebuildActions>> $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);

View File

@@ -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();

View File

@@ -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.

View File

@@ -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<string,array<string,mixed>>
*/
/** @var ?array<string, array<string, mixed>> */
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<string,array<string,mixed>>
* @return array<string, array<string, mixed>>
*/
public function getData(bool $reload = false): array
{
@@ -92,7 +85,7 @@ class OrmMetadataData
}
if ($this->useCache && $this->dataCache->has($this->cacheKey) && !$reload) {
/** @var array<string,array<string,mixed>> $data */
/** @var array<string, array<string, mixed>> $data */
$data = $this->dataCache->get($this->cacheKey);
$this->data = $data;

View File

@@ -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{

View File

@@ -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);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\ORM\PDO;
use Espo\ORM\DatabaseParams;
use PDO;
use RuntimeException;
class MysqlPDOFactory implements PDOFactory
{
public function create(DatabaseParams $databaseParams): PDO
{
$platform = strtolower($databaseParams->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;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\ORM\PDO;
use Espo\ORM\DatabaseParams;
use PDO;
interface PDOFactory
{
public function create(DatabaseParams $databaseParams): PDO;
}

View File

@@ -4,5 +4,8 @@
},
"queryComposerClassNameMap": {
"Mysql": "Espo\\ORM\\QueryComposer\\MysqlQueryComposer"
},
"pdoFactoryClassNameMap": {
"Mysql": "Espo\\ORM\\PDO\\MysqlPDOFactory"
}
}

View File

@@ -33,14 +33,11 @@ use Espo\Core\Utils\Metadata;
use Espo\Core\Utils\Language;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Json;
use Espo\Core\{
Exceptions\BadRequest,
Exceptions\Error,
Exceptions\Conflict,
Utils\Metadata\Helper as MetadataHelper,
Utils\Util,
};
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Conflict;
use Espo\Core\Exceptions\Error;
use Espo\Core\Utils\Metadata\Helper as MetadataHelper;
use Espo\Core\Utils\Util;
use stdClass;
@@ -49,12 +46,6 @@ use stdClass;
*/
class FieldManager
{
private InjectableFactory $injectableFactory;
private $metadata;
private $language;
private $baseLanguage;
private $metadataHelper;
protected bool $isChanged = false;
/**
@@ -85,18 +76,12 @@ class FieldManager
const MAX_NAME_LENGTH = 100;
public function __construct(
InjectableFactory $injectableFactory,
Metadata $metadata,
Language $language,
Language $baseLanguage
) {
$this->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<string,mixed>

View File

@@ -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');

View File

@@ -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();

View File

@@ -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'