Skip to content

Commit

Permalink
Merge pull request #124 from yethee/conn-params
Browse files Browse the repository at this point in the history
Restore support for connecting using DSN string
  • Loading branch information
Slamdunk authored Feb 15, 2024
2 parents f0c5be2 + 167fec5 commit ca2f38d
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 13 deletions.
63 changes: 60 additions & 3 deletions example/full-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare(strict_types=1);

use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Driver as DbalDriver;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Driver\SQLite3\Driver;
Expand All @@ -13,6 +14,7 @@
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Psr\Container\ContainerInterface;
use Roave\PsrContainerDoctrine\EntityManagerFactory;
use Roave\PsrContainerDoctrine\Migrations\CommandFactory;
use Roave\PsrContainerDoctrine\Migrations\ConfigurationLoaderFactory;
Expand Down Expand Up @@ -60,19 +62,67 @@
'connection' => [
'orm_default' => [
'driver_class' => Driver::class,
// 'wrapper_class' => null,
'pdo' => null,
'configuration' => 'orm_default', // Actually defaults to the connection config key, not hard-coded
'params' => ['serverVersion' => 'mariadb-11.0'],
'doctrine_type_mappings' => [],
],
'orm_default_with_url' => [
'configuration' => 'orm_default',
'driver_class' => Driver::class,
'wrapper_class' => PrimaryReadReplicaConnection::class,
'params' => [
'primary' => ['url' => '//app:secr3t@primary.cluster:3306/foo?charset=utf8mb4'],
'replica' => [
['url' => '//replica1.cluster:3306/foo?charset=utf8mb4'],
['url' => '//replica2.cluster:3306/foo?charset=utf8mb4'],
],
],
],
'orm_default_with_array' => [
'configuration' => 'orm_default',
'driver_class' => Driver::class,
'wrapper_class' => PrimaryReadReplicaConnection::class,
'params' => [
'primary' => [
'user' => 'app',
'password' => 'secr3t',
'host' => 'primary.cluster',
'port' => 3306,
'dbname' => 'foo',
'charset' => 'utf8mb4',
],
'replica' => [
[
'host' => 'replica1.cluster',
'port' => 3306,
'dbname' => 'foo',
'charset' => 'utf8mb4',
],
[
'host' => 'replica2.cluster',
'port' => 3306,
'dbname' => 'foo',
'charset' => 'utf8mb4',
],
],
],
],
],
'entity_manager' => [
'orm_default' => [
'connection' => 'orm_default', // Actually defaults to the entity manager config key, not hard-coded
'configuration' => 'orm_default', // Actually defaults to the entity manager config key, not hard-coded
'event_manager' => 'orm_default', // Actually defaults to the connection config key, not hard-coded
],
'orm_default_with_url' => [
'connection' => 'orm_default_with_url',
'configuration' => 'orm_default', // Actually defaults to the entity manager config key, not hard-coded
'event_manager' => 'orm_default', // Actually defaults to the connection config key, not hard-coded
],
'orm_default_with_array' => [
'connection' => 'orm_default_with_array',
'configuration' => 'orm_default', // Actually defaults to the entity manager config key, not hard-coded
'event_manager' => 'orm_default', // Actually defaults to the connection config key, not hard-coded
],
],
'event_manager' => [
'orm_default' => [
Expand Down Expand Up @@ -137,6 +187,13 @@
DependencyFactory::class => DependencyFactoryFactory::class,
ConfigurationLoader::class => ConfigurationLoaderFactory::class,

'doctrine.entity_manager.orm_default_with_url' => static function (ContainerInterface $container): EntityManagerInterface {
return (new EntityManagerFactory('orm_default_with_url'))->__invoke($container);
},
'doctrine.entity_manager.orm_default_with_array' => static function (ContainerInterface $container): EntityManagerInterface {
return (new EntityManagerFactory('orm_default_with_url'))->__invoke($container);
},

'my_schema_assets_filter' => static function (): callable {
/**
* Filter out assets (table, sequence) by name from Schema
Expand Down
10 changes: 10 additions & 0 deletions example/laminas-servicemanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@
if (is_object($object)) {
echo $object::class . "\n"; // Doctrine\ORM\EntityManager
}

$object = $container->get('doctrine.entity_manager.orm_default_with_url');
if (is_object($object)) {
echo $object::class . "\n"; // Doctrine\ORM\EntityManager
}

$object = $container->get('doctrine.entity_manager.orm_default_with_array');
if (is_object($object)) {
echo $object::class . "\n"; // Doctrine\ORM\EntityManager
}
2 changes: 1 addition & 1 deletion example/minimal-config-custom-key.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'connection' => [
'orm_custom_key' => [
'driver_class' => Driver::class,
'params' => ['url' => 'sqlite::memory:'],
'params' => ['url' => 'sqlite3:///:memory:'],
],
],
'driver' => [
Expand Down
2 changes: 1 addition & 1 deletion example/minimal-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'connection' => [
'orm_default' => [
'driver_class' => Driver::class,
'params' => ['url' => 'sqlite::memory:'],
'params' => ['url' => 'sqlite3:///:memory:'],
],
],
'driver' => [
Expand Down
53 changes: 48 additions & 5 deletions src/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PdoMysqlDriver;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Tools\DsnParser;
use Doctrine\DBAL\Types\Type;
use Psr\Container\ContainerInterface;
use SensitiveParameter;

use function is_string;
use function array_map;
use function array_merge;

/** @extends AbstractFactory<Connection> */
final class ConnectionFactory extends AbstractFactory
Expand All @@ -22,11 +25,25 @@ protected function createWithConfig(ContainerInterface $container, string $confi
$this->registerTypes($container);

$config = $this->retrieveConfig($container, $configKey, 'connection');
$params = $config['params'] + [
$params = $this->parseDatabaseUrl($config['params'] + [
'driverClass' => $config['driver_class'],
'wrapperClass' => $config['wrapper_class'],
'pdo' => is_string($config['pdo']) ? $container->get($config['pdo']) : $config['pdo'],
];
]);

if (isset($params['driver'])) {
unset($params['driverClass']);
}

if (isset($params['primary'])) {
$params['primary'] = $this->parseDatabaseUrl($params['primary']);
}

if (isset($params['replica'])) {
$params['replica'] = array_map(
fn (array $params): array => $this->parseDatabaseUrl($params),
$params['replica'],
);
}

$connection = DriverManager::getConnection(
$params,
Expand Down Expand Up @@ -54,7 +71,6 @@ protected function getDefaultConfig(string $configKey): array
return [
'driver_class' => PdoMysqlDriver::class,
'wrapper_class' => null,
'pdo' => null,
'configuration' => $configKey,
'params' => [],
'doctrine_mapping_types' => [],
Expand Down Expand Up @@ -83,4 +99,31 @@ private function registerTypes(ContainerInterface $container): void
Type::addType($name, $className);
}
}

/**
* @param array<array-key, mixed> $params
*
* @return array<array-key, mixed>
*/
private function parseDatabaseUrl(
#[SensitiveParameter]
array $params,
): array {
if (! isset($params['url'])) {
return $params;
}

$parser = new DsnParser();

$parsedParams = $parser->parse($params['url']);
unset($params['url']);

$result = array_merge($params, $parsedParams);

if (isset($result['driver']) && isset($result['driverClass'])) {
unset($result['driverClass']);
}

return $result;
}
}
62 changes: 60 additions & 2 deletions test/ConnectionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace RoaveTest\PsrContainerDoctrine;

use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\API\MySQL\ExceptionConverter;
use Doctrine\DBAL\Driver\PDO\MySQL\Driver as PDOMySQLDriver;
Expand Down Expand Up @@ -88,7 +89,6 @@ public function testDefaults(): void
self::assertSame([
'driverClass' => PDOSqliteDriver::class,
'wrapperClass' => null,
'pdo' => null,
], $connection->getParams());
}

Expand Down Expand Up @@ -119,7 +119,65 @@ public function testParamsInjection(): void
'username' => 'foo',
'driverClass' => PDOSqliteDriver::class,
'wrapperClass' => null,
'pdo' => null,
], $connection->getParams());
}

public function testUrlParam(): void
{
$factory = new ConnectionFactory();
$connection = $factory($this->buildContainer('orm_default', 'orm_default', [
'params' => ['url' => 'sqlite3:///:memory:'],
]));

self::assertSame([
'wrapperClass' => null,
'driver' => 'sqlite3',
'host' => 'localhost',
'memory' => true,
], $connection->getParams());
}

public function testPrimaryReplicaParams(): void
{
$factory = new ConnectionFactory();
$connection = $factory($this->buildContainer('orm_default', 'orm_default', [
'params' => [
'driver' => 'pdo_mysql',
'serverVersion' => '8.0.29',
'primary' => ['url' => '//localhost:4486/foo?charset=utf8mb4'],
'replica' => [
['url' => '//replica1:3306/foo'],
['url' => '//replica2:3306/foo'],
],
],
'wrapper_class' => PrimaryReadReplicaConnection::class,
]));

self::assertSame([
'driver' => 'pdo_mysql',
'serverVersion' => '8.0.29',
'primary' => [
'host' => 'localhost',
'port' => 4486,
'dbname' => 'foo',
'charset' => 'utf8mb4',
'driver' => 'pdo_mysql',
],
'replica' => [
[
'host' => 'replica1',
'port' => 3306,
'dbname' => 'foo',
'driver' => 'pdo_mysql',
],
[
'host' => 'replica2',
'port' => 3306,
'dbname' => 'foo',
'driver' => 'pdo_mysql',
],
],
'wrapperClass' => PrimaryReadReplicaConnection::class,
], $connection->getParams());
}

Expand Down
2 changes: 1 addition & 1 deletion test/example/check-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ check () {
fi
}

check example/laminas-servicemanager.php $'Doctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager'
check example/laminas-servicemanager.php $'Doctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager'
check example/illuminate-container.php $'Doctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager\nDoctrine\\ORM\\EntityManager'

0 comments on commit ca2f38d

Please sign in to comment.