Please give ConnectionFactory an interface
Feature Request
What
With your version 3 you made ConnectionFactory final, forcing us to wrap the service instead of extending it. This works fine, but there is no interface we could be using.
Why
We use a custom ConnectionFactory to inject additional services into our custom connection that we need.
Also we register some additional and complex database types in there that are not compatible with ConnectionFactory::initializeTypes.
How
Implementing an interface like this would already suffice:
interface ConnectionFactoryInterface {
public function createConnection(array $params, ?Configuration $config = null, array $mappingTypes = []): Connection;
}
Thank you
What is your use case for replacing the ConnectionFactory ?
As I said, for one we need to inject services into our custom connection. The bundle allows to override the connection's class with wrapper_class, but not as service with extra dependencies to my knowledge.
And we register special types, e.g. a native \UnitEnum type which becomes a MySQL enum.
E.g. from our user entity, this UserType is a native \UnitEnum that is automatically injected as doctrine type:
#[Column(type: UserType::class, options: ['default' => 'regular'])]
private UserType $type = UserType::regular;
if you are interested, this is our implementation
<?php
declare(strict_types=1);
namespace Octaved\Doctrine\DBAL;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Type;
use Octaved\Doctrine\DBAL\Type\IEnumSet;
use Octaved\Doctrine\DBAL\Type\JsonDecoratorType;
use Octaved\Doctrine\DBAL\Type\NativeEnumSetType;
use Octaved\Doctrine\DBAL\Type\NativeEnumType;
use Octaved\Doctrine\DeadlockHandler;
use Octaved\Utilities\Decorator\StdClassDecorator;
class ConnectionFactory {
/**
* Static because we have two connections with the same types and Type::addType is static anyway.
*/
private static bool $typesAdded = false;
public function __construct(
private readonly \Doctrine\Bundle\DoctrineBundle\ConnectionFactory $inner,
private readonly DeadlockHandler $deadlockHandler,
/** @var string[] */
private readonly array $packageTypes,
/** @var string[] */
private readonly array $fqnTypes,
) {
}
public function createConnection(array $params, ?Configuration $config = null, array $mappingTypes = []): Connection {
$this->registerPackageTypes();
$connection = $this->inner->createConnection($params, $config, $mappingTypes);
if ($connection instanceof Con) {
$connection->setDeadlockHandler($this->deadlockHandler);
}
return $connection;
}
private function setType(string $type, string $class): void {
if (Type::hasType($type)) {
Type::overrideType($type, $class);
} else {
Type::addType($type, $class);
}
}
private function registerPackageTypes(): void {
if (!self::$typesAdded) {
foreach ($this->packageTypes as $type => $class) {
$this->setType($type, $class);
}
foreach ($this->fqnTypes as $fqnType) {
if (is_subclass_of($fqnType, \UnitEnum::class)) {
NativeEnumType::register($fqnType, $fqnType);
} elseif (is_subclass_of($fqnType, IEnumSet::class)) {
NativeEnumSetType::register($fqnType, $fqnType::getEnumClass());
} elseif (is_subclass_of($fqnType, StdClassDecorator::class)) {
JsonDecoratorType::register($fqnType);
} elseif (is_subclass_of($fqnType, Type::class)) {
$this->setType($fqnType, $fqnType);
}
}
self::$typesAdded = true;
}
}
}