diff --git a/docs/Documentation.md b/docs/Documentation.md index aae9e77..56f1625 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -90,10 +90,53 @@ mongo_db_bundle: client_name: ~ database_name: ~ + # Service reference to provide URI options - see example below + uriOptions: 'App\Services\UriOptionsProvider' # default null + # Service reference to provide driver options - see example below driverOptions: 'App\Services\DriverOptionsProvider' # default null ``` +### Uri options + +You might need to specify some URI options for constructing the `MongoDB\Client`. Read the [reference] for a complete +explanation of all the available options. + +Implement `UriOptionsInterface` and declare the class as a Symfony service. + +```php +namespace App\Services; + +use Facile\MongoDbBundle\Services\UriOptions\UriOptionsInterface; + +final class MyCustomUriOptionsProvider implements UriOptionsInterface +{ + private const APPNAME = 'APPNAME'; + + public function buildUriOptions(array $clientConfiguration): array + { + return array_merge( + $clientConfiguration, + ['appname' => self::APPNAME] + ); + } +} +``` + +```yaml +# config/services.yaml +App\Services\MyCustomUriOptionsProvider: +``` + +Then use its service id as value of `uriOptions` in the bundle configuration. + +```yml +# config/packages/facile_it_mongodb.yaml +mongo_db_bundle: + uriOptions: 'App\Services\MyCustomUriOptionsProvider' + # ... +``` + ### Driver options You might need to specify some driver options for constructing the `MongoDB\Client`. Read the [reference] for a complete diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f960265..d7884a0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2,7 +2,7 @@ parameters: ignoreErrors: - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" - count: 7 + count: 8 path: src/DependencyInjection/Configuration.php - diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index c690fb2..e1b515d 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -26,6 +26,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addDataCollection($rootBuilder->children()); $this->addClients($rootBuilder->children()); $this->addConnections($rootBuilder->children()); + $this->addUriOptions($rootBuilder->children()); $this->addDriversOption($rootBuilder->children()); return $treeBuilder; @@ -106,6 +107,12 @@ private function addClientsHosts(NodeBuilder $builder): void ->defaultValue(27_017); } + private function addUriOptions(NodeBuilder $builder): void + { + $builder + ->scalarNode('uriOptions'); + } + private function addDriversOption(NodeBuilder $builder): void { $builder diff --git a/src/DependencyInjection/MongoDbBundleExtension.php b/src/DependencyInjection/MongoDbBundleExtension.php index 8462061..9786e86 100644 --- a/src/DependencyInjection/MongoDbBundleExtension.php +++ b/src/DependencyInjection/MongoDbBundleExtension.php @@ -58,6 +58,7 @@ private function defineClientRegistry(array $config, bool $debug): void [ new Reference('facile_mongo_db.event_dispatcher'), $debug, + $this->defineUriOptionsFactory($config), $this->defineDriverOptionsFactory($config), ] ); @@ -111,6 +112,11 @@ private function attachDataCollectionListenerToEventManager(): void ); } + private function defineUriOptionsFactory(array $config): ?Reference + { + return isset($config['uriOptions']) ? new Reference($config['uriOptions']) : null; + } + private function defineDriverOptionsFactory(array $config): ?Reference { return isset($config['driverOptions']) ? new Reference($config['driverOptions']) : null; diff --git a/src/Services/ClientRegistry.php b/src/Services/ClientRegistry.php index 9ed6088..858e448 100644 --- a/src/Services/ClientRegistry.php +++ b/src/Services/ClientRegistry.php @@ -8,6 +8,7 @@ use Facile\MongoDbBundle\Event\ConnectionEvent; use Facile\MongoDbBundle\Models\ClientConfiguration; use Facile\MongoDbBundle\Services\DriverOptions\DriverOptionsInterface; +use Facile\MongoDbBundle\Services\UriOptions\UriOptionsInterface; use MongoDB\Client; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -28,16 +29,20 @@ final class ClientRegistry private EventDispatcherInterface $eventDispatcher; + private ?UriOptionsInterface $uriOptionsService; + private ?DriverOptionsInterface $driverOptionsService; public function __construct( EventDispatcherInterface $eventDispatcher, bool $debug, + ?UriOptionsInterface $uriOptionsService, ?DriverOptionsInterface $driverOptionsService ) { $this->debug = $debug; $this->eventDispatcher = $eventDispatcher; $this->driverOptionsService = $driverOptionsService; + $this->uriOptionsService = $uriOptionsService; } public function addClientsConfigurations(array $configurations): void @@ -53,6 +58,16 @@ private function buildClientConfiguration(array $conf): ClientConfiguration $conf['uri'] = $this->buildConnectionUri($conf['hosts']); } + $conf['uriOptions'] = [ + 'replicaSet' => $conf['replicaSet'], + 'ssl' => $conf['ssl'], + 'connectTimeoutMS' => $conf['connectTimeoutMS'], + 'readPreference' => $conf['readPreference'], + ]; + if ($this->uriOptionsService instanceof UriOptionsInterface) { + $conf['uriOptions'] = $this->uriOptionsService->buildUriOptions($conf['uriOptions']); + } + $conf['driverOptions'] = []; if ($this->driverOptionsService instanceof DriverOptionsInterface) { $conf['driverOptions'] = $this->driverOptionsService->buildDriverOptions($conf); @@ -63,12 +78,7 @@ private function buildClientConfiguration(array $conf): ClientConfiguration $conf['username'], $conf['password'], $conf['authSource'], - [ - 'replicaSet' => $conf['replicaSet'], - 'ssl' => $conf['ssl'], - 'connectTimeoutMS' => $conf['connectTimeoutMS'], - 'readPreference' => $conf['readPreference'], - ], + $conf['uriOptions'], $conf['driverOptions'] ); } @@ -122,4 +132,12 @@ private function buildClient(string $clientName, string $uri, array $options, ar return new Client($uri, $options, $driverOptions); } + + /** + * @return ClientConfiguration[] + */ + public function getConfigurations(): array + { + return $this->configurations; + } } diff --git a/src/Services/UriOptions/UriOptionsInterface.php b/src/Services/UriOptions/UriOptionsInterface.php new file mode 100644 index 0000000..dc95bd3 --- /dev/null +++ b/src/Services/UriOptions/UriOptionsInterface.php @@ -0,0 +1,22 @@ +createEventDispatcherMock(), false, null); + $registry = new ClientRegistry($this->createEventDispatcherMock(), false, null, null); $testConf = [ 'test_client' => [ @@ -43,7 +44,7 @@ public function test_client_connection_url_provided_manually(): void public function test_client_connection_url_generation_singlehost(): void { - $registry = new ClientRegistry($this->createEventDispatcherMock(), false, null); + $registry = new ClientRegistry($this->createEventDispatcherMock(), false, null, null); $testConf = [ 'test_client' => [ @@ -71,7 +72,7 @@ public function test_client_connection_url_generation_singlehost(): void public function test_client_connection_url_generation_multihost(): void { - $registry = new ClientRegistry($this->createEventDispatcherMock(), false, null); + $registry = new ClientRegistry($this->createEventDispatcherMock(), false, null, null); $testConf = [ 'test_client' => [ @@ -96,6 +97,37 @@ public function test_client_connection_url_generation_multihost(): void $this->assertEquals('mongodb://host1:8080,host2:8081', $client->__debugInfo()['uri']); } + public function test_client_connection_url_generation_with_custom_uri_options(): void + { + $customUriOptions = ['appname' => 'APPNAME']; + $uriOptionsService = $this->getUriOptionsService($customUriOptions); + $registry = new ClientRegistry($this->createEventDispatcherMock(), false, $uriOptionsService, null); + + $testConf = [ + 'test_client' => [ + 'hosts' => [], + 'uri' => 'mongodb://user:password@host1:27017', + 'username' => '', + 'password' => '', + 'authSource' => null, + 'replicaSet' => 'testReplica', + 'ssl' => true, + 'connectTimeoutMS' => 3_000, + 'readPreference' => 'primary', + ], + ]; + + $registry->addClientsConfigurations($testConf); + $client = $registry->getClient('test_client', 'testdb'); + + $this->assertEquals('mongodb://user:password@host1:27017', $client->__debugInfo()['uri']); + $this->assertEquals(['test_client.testdb'], $registry->getClientNames()); + self::assertArrayHasKey('test_client', $registry->getConfigurations()); + self::assertObjectHasProperty('options', $registry->getConfigurations()['test_client']); + self::assertArrayHasKey('appname', $registry->getConfigurations()['test_client']->getOptions()); + $this->assertEquals('APPNAME', $registry->getConfigurations()['test_client']->getOptions()['appname']); + } + private function createEventDispatcherMock(): EventDispatcherInterface { $eventDispatcher = $this->prophesize(EventDispatcherInterface::class); @@ -113,4 +145,21 @@ private function createEventDispatcherMock(): EventDispatcherInterface return $eventDispatcher->reveal(); } + + private function getUriOptionsService($customUriOptions): UriOptionsInterface + { + return new class ($customUriOptions) implements UriOptionsInterface { + private array $customUriOptions; + + public function __construct($customUriOptions) + { + $this->customUriOptions = $customUriOptions; + } + + public function buildUriOptions(array $clientConfiguration): array + { + return array_merge($clientConfiguration, $this->customUriOptions); + } + }; + } }