Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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

-
Expand Down
7 changes: 7 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/DependencyInjection/MongoDbBundleExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]
);
Expand Down Expand Up @@ -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;
Expand Down
30 changes: 24 additions & 6 deletions src/Services/ClientRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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']
);
}
Expand Down Expand Up @@ -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;
}
}
22 changes: 22 additions & 0 deletions src/Services/UriOptions/UriOptionsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Facile\MongoDbBundle\Services\UriOptions;

use MongoDB\Client;

interface UriOptionsInterface
{
/**
* It creates an array of options for constructing a MongoDB\Client.
*
* @param array $clientConfiguration client's bundle configuration for which the options are needed
*
* @return array Options for MongoDB\Client
*
* @see Client
* @see http://php.net/manual/en/mongodb-driver-manager.construct.php
*/
public function buildUriOptions(array $clientConfiguration): array;
}
55 changes: 52 additions & 3 deletions tests/Unit/Services/ClientRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Facile\MongoDbBundle\Tests\Unit\Services;

use Facile\MongoDbBundle\Services\UriOptions\UriOptionsInterface;
use Prophecy\PhpUnit\ProphecyTrait;
use Facile\MongoDbBundle\Event\ConnectionEvent;
use Facile\MongoDbBundle\Services\ClientRegistry;
Expand All @@ -17,7 +18,7 @@ class ClientRegistryTest extends TestCase

public function test_client_connection_url_provided_manually(): void
{
$registry = new ClientRegistry($this->createEventDispatcherMock(), false, null);
$registry = new ClientRegistry($this->createEventDispatcherMock(), false, null, null);

$testConf = [
'test_client' => [
Expand All @@ -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' => [
Expand Down Expand Up @@ -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' => [
Expand All @@ -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);
Expand All @@ -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);
}
};
}
}