From d3f25fb2cb12300d7c0ab0cf3b0b024f5cdaee60 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Fri, 8 Dec 2023 07:52:36 +0100 Subject: [PATCH 1/6] Resolve service references for codec collection options --- src/Attribute/AutowireCollection.php | 8 +++++- .../Unit/Attribute/AutowireCollectionTest.php | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Attribute/AutowireCollection.php b/src/Attribute/AutowireCollection.php index 957d77b..4994942 100644 --- a/src/Attribute/AutowireCollection.php +++ b/src/Attribute/AutowireCollection.php @@ -30,6 +30,7 @@ use Symfony\Component\DependencyInjection\Reference; use function is_string; +use function ltrim; use function sprintf; /** @@ -59,12 +60,17 @@ public function __construct( public function buildDefinition(mixed $value, ?string $type, ReflectionParameter $parameter): Definition { + $options = $this->options; + if (isset($options['codec']) && is_string($options['codec'])) { + $options['codec'] = new Reference(ltrim($options['codec'], '@')); + } + return (new Definition(is_string($this->lazy) ? $this->lazy : ($type ?: Collection::class))) ->setFactory($value) ->setArguments([ $this->database ?? sprintf('%%%s.default_database%%', $this->serviceId), $this->collection ?? $parameter->getName(), - $this->options, + $options, ]) ->setLazy($this->lazy); } diff --git a/tests/Unit/Attribute/AutowireCollectionTest.php b/tests/Unit/Attribute/AutowireCollectionTest.php index 5f62a79..2cfe13a 100644 --- a/tests/Unit/Attribute/AutowireCollectionTest.php +++ b/tests/Unit/Attribute/AutowireCollectionTest.php @@ -109,4 +109,31 @@ static function (Collection $priceReports): void { $this->assertSame('priceReports', $definition->getArgument(1)); $this->assertSame(['foo' => 'bar'], $definition->getArgument(2)); } + + public function testWithCodecOption(): void + { + $autowire = new AutowireCollection( + database: 'mydb', + client: 'default', + options: ['foo' => 'bar', 'codec' => '@my_codec'], + ); + + $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); + + $definition = $autowire->buildDefinition( + value: $autowire->value, + type: Collection::class, + parameter: new ReflectionParameter( + static function (Collection $priceReports): void { + }, + 'priceReports', + ), + ); + + $this->assertSame(Collection::class, $definition->getClass()); + $this->assertEquals($autowire->value, $definition->getFactory()); + $this->assertSame('mydb', $definition->getArgument(0)); + $this->assertSame('priceReports', $definition->getArgument(1)); + $this->assertEquals(['foo' => 'bar', 'codec' => new Reference('my_codec')], $definition->getArgument(2)); + } } From 57d6f9ce74611b383d59750612b7ae3ea165f970 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Sat, 9 Dec 2023 12:50:52 +0100 Subject: [PATCH 2/6] Add codec option to AutowireCollection --- src/Attribute/AutowireCollection.php | 6 ++ .../Unit/Attribute/AutowireCollectionTest.php | 60 ++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Attribute/AutowireCollection.php b/src/Attribute/AutowireCollection.php index 4994942..4dca3d1 100644 --- a/src/Attribute/AutowireCollection.php +++ b/src/Attribute/AutowireCollection.php @@ -23,6 +23,7 @@ use Attribute; use MongoDB\Bundle\DependencyInjection\MongoDBExtension; use MongoDB\Client; +use MongoDB\Codec\DocumentCodec; use MongoDB\Collection; use ReflectionParameter; use Symfony\Component\DependencyInjection\Attribute\AutowireCallable; @@ -45,6 +46,7 @@ public function __construct( private readonly ?string $collection = null, private readonly ?string $database = null, ?string $client = null, + private readonly string|DocumentCodec|null $codec = null, private readonly array $options = [], bool|string $lazy = false, ) { @@ -61,6 +63,10 @@ public function __construct( public function buildDefinition(mixed $value, ?string $type, ReflectionParameter $parameter): Definition { $options = $this->options; + if ($this->codec !== null) { + $options['codec'] = $this->codec; + } + if (isset($options['codec']) && is_string($options['codec'])) { $options['codec'] = new Reference(ltrim($options['codec'], '@')); } diff --git a/tests/Unit/Attribute/AutowireCollectionTest.php b/tests/Unit/Attribute/AutowireCollectionTest.php index 2cfe13a..8c82588 100644 --- a/tests/Unit/Attribute/AutowireCollectionTest.php +++ b/tests/Unit/Attribute/AutowireCollectionTest.php @@ -20,8 +20,12 @@ namespace MongoDB\Bundle\Tests\Unit\Attribute; +use MongoDB\BSON\Document; use MongoDB\Bundle\Attribute\AutowireCollection; use MongoDB\Client; +use MongoDB\Codec\DecodeIfSupported; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Codec\EncodeIfSupported; use MongoDB\Collection; use PHPUnit\Framework\TestCase; use ReflectionParameter; @@ -115,7 +119,8 @@ public function testWithCodecOption(): void $autowire = new AutowireCollection( database: 'mydb', client: 'default', - options: ['foo' => 'bar', 'codec' => '@my_codec'], + codec: '@my_codec', + options: ['foo' => 'bar'], ); $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); @@ -136,4 +141,57 @@ static function (Collection $priceReports): void { $this->assertSame('priceReports', $definition->getArgument(1)); $this->assertEquals(['foo' => 'bar', 'codec' => new Reference('my_codec')], $definition->getArgument(2)); } + + public function testWithCodecInstanceParameter(): void + { + $codec = new class implements DocumentCodec { + use DecodeIfSupported; + use EncodeIfSupported; + + public function canDecode($value): bool + { + return $value instanceof Document; + } + + public function canEncode($value): bool + { + return $value instanceof Document; + } + + public function decode($value): Document + { + return $value; + } + + public function encode($value): Document + { + return $value; + } + }; + + $autowire = new AutowireCollection( + database: 'mydb', + client: 'default', + codec: $codec, + options: ['foo' => 'bar'], + ); + + $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); + + $definition = $autowire->buildDefinition( + value: $autowire->value, + type: Collection::class, + parameter: new ReflectionParameter( + static function (Collection $priceReports): void { + }, + 'priceReports', + ), + ); + + $this->assertSame(Collection::class, $definition->getClass()); + $this->assertEquals($autowire->value, $definition->getFactory()); + $this->assertSame('mydb', $definition->getArgument(0)); + $this->assertSame('priceReports', $definition->getArgument(1)); + $this->assertEquals(['foo' => 'bar', 'codec' => $codec], $definition->getArgument(2)); + } } From d1cc54d6c604cc576700e138e118beea3d4590c5 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Sat, 9 Dec 2023 12:58:34 +0100 Subject: [PATCH 3/6] Document codec usage --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47cee05..e51890e 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ class MyService } ``` -## Database and Collection Usage +## Database Usage The client service provides access to databases and collections. You can access a database by calling the `selectDatabase` method, passing the database name and potential options: @@ -183,6 +183,8 @@ class MyService } ``` +## Collection Usage + To inject a collection, you can either call the `selectCollection` method on a `Client` or `Database` instance. For convenience, the `#[AutowireCollection]` attribute provides a quicker alternative: @@ -261,3 +263,22 @@ class MyService ) {} } ``` + +### Using a Codec + +To use a custom codec for a collection, you can specify the `codec` option in the `AutowireCollection` attribute. The +codec class must implement the `MongoDB\Codec\DocumentCodec` interface. You can either pass an instance of the selected +codec, or pass a service reference to the codec: + +```php +use MongoDB\Bundle\Attribute\AutowireCollection; +use MongoDB\Collection; + +class MyService +{ + public function __construct( + #[AutowireCollection(codec: MyCodec::class)] + private Collection $myCollection, + ) {} +} +``` From 1761524547578070f2f1aa29ade5071c1e52c122 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Sat, 9 Dec 2023 13:05:26 +0100 Subject: [PATCH 4/6] Bump MongoDB dependency to 1.17 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1a73378..72f42e8 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ ], "require": { "php": ">=8.1", - "mongodb/mongodb": "^1.16", + "mongodb/mongodb": "^1.17", "symfony/config": "^6.3 || ^7.0", "symfony/console": "^6.3 || ^7.0", "symfony/dependency-injection": "^6.3.5 || ^7.0", From cf26aca5acd0707a960bbb1958fa263e5342ec32 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Sat, 9 Dec 2023 14:18:39 +0100 Subject: [PATCH 5/6] Allow passing all options to AutowireCollection and AutowireDatabase --- README.md | 18 +++-- src/Attribute/AutowireCollection.php | 20 +++-- src/Attribute/AutowireDatabase.php | 19 ++++- tests/Unit/Attribute/AttributeTestCase.php | 72 +++++++++++++++++ .../Unit/Attribute/AutowireCollectionTest.php | 80 ++----------------- tests/Unit/Attribute/AutowireDatabaseTest.php | 31 +++++-- 6 files changed, 146 insertions(+), 94 deletions(-) create mode 100644 tests/Unit/Attribute/AttributeTestCase.php diff --git a/README.md b/README.md index e51890e..2cf4295 100644 --- a/README.md +++ b/README.md @@ -264,20 +264,28 @@ class MyService } ``` -### Using a Codec +## Specifying options -To use a custom codec for a collection, you can specify the `codec` option in the `AutowireCollection` attribute. The -codec class must implement the `MongoDB\Codec\DocumentCodec` interface. You can either pass an instance of the selected -codec, or pass a service reference to the codec: +When using the `AutowireDatabase` or `AutowireCollection` attributes, you can specify additional options for the +resulting instances. You can pass the following options: +|| Option || Accepted type || +| `codec` | `DocumentCodec` instance | +| `typeMap`| `array` containing type map information | +| `readPreference` | `MongoDB\Driver\ReadPreference` instance | +| `writeConcern` | `MongoDB\Driver\writeConcern` instance | +| `readConcern` | `MongoDB\Driver\ReadConcern` instance | + +In addition to passing an instance, you can also pass a service reference by specifying a string for the given option: ```php use MongoDB\Bundle\Attribute\AutowireCollection; use MongoDB\Collection; +use MongoDB\Driver\ReadPreference; class MyService { public function __construct( - #[AutowireCollection(codec: MyCodec::class)] + #[AutowireCollection(codec: Codec::class, readPreference: new ReadPreference('secondary'))] private Collection $myCollection, ) {} } diff --git a/src/Attribute/AutowireCollection.php b/src/Attribute/AutowireCollection.php index 4dca3d1..fcce14e 100644 --- a/src/Attribute/AutowireCollection.php +++ b/src/Attribute/AutowireCollection.php @@ -25,6 +25,9 @@ use MongoDB\Client; use MongoDB\Codec\DocumentCodec; use MongoDB\Collection; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\WriteConcern; use ReflectionParameter; use Symfony\Component\DependencyInjection\Attribute\AutowireCallable; use Symfony\Component\DependencyInjection\Definition; @@ -47,7 +50,10 @@ public function __construct( private readonly ?string $database = null, ?string $client = null, private readonly string|DocumentCodec|null $codec = null, - private readonly array $options = [], + private readonly string|array|null $typeMap = null, + private readonly string|ReadPreference|null $readPreference = null, + private readonly string|WriteConcern|null $writeConcern = null, + private readonly string|ReadConcern|null $readConcern = null, bool|string $lazy = false, ) { $this->serviceId = $client === null @@ -62,13 +68,11 @@ public function __construct( public function buildDefinition(mixed $value, ?string $type, ReflectionParameter $parameter): Definition { - $options = $this->options; - if ($this->codec !== null) { - $options['codec'] = $this->codec; - } - - if (isset($options['codec']) && is_string($options['codec'])) { - $options['codec'] = new Reference(ltrim($options['codec'], '@')); + $options = []; + foreach (['codec', 'typeMap', 'readPreference', 'writeConcern', 'readConcern'] as $option) { + if ($this->$option !== null) { + $options[$option] = is_string($this->$option) ? new Reference($this->$option) : $this->$option; + } } return (new Definition(is_string($this->lazy) ? $this->lazy : ($type ?: Collection::class))) diff --git a/src/Attribute/AutowireDatabase.php b/src/Attribute/AutowireDatabase.php index c46b17a..0d64c30 100644 --- a/src/Attribute/AutowireDatabase.php +++ b/src/Attribute/AutowireDatabase.php @@ -23,7 +23,11 @@ use Attribute; use MongoDB\Bundle\DependencyInjection\MongoDBExtension; use MongoDB\Client; +use MongoDB\Codec\DocumentCodec; use MongoDB\Database; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\WriteConcern; use ReflectionParameter; use Symfony\Component\DependencyInjection\Attribute\AutowireCallable; use Symfony\Component\DependencyInjection\Definition; @@ -43,7 +47,11 @@ final class AutowireDatabase extends AutowireCallable public function __construct( private readonly ?string $database = null, ?string $client = null, - private readonly array $options = [], + private readonly string|DocumentCodec|null $codec = null, + private readonly string|array|null $typeMap = null, + private readonly string|ReadPreference|null $readPreference = null, + private readonly string|WriteConcern|null $writeConcern = null, + private readonly string|ReadConcern|null $readConcern = null, bool|string $lazy = false, ) { $this->serviceId = $client === null @@ -58,11 +66,18 @@ public function __construct( public function buildDefinition(mixed $value, ?string $type, ReflectionParameter $parameter): Definition { + $options = []; + foreach (['codec', 'typeMap', 'readPreference', 'writeConcern', 'readConcern'] as $option) { + if ($this->$option !== null) { + $options[$option] = is_string($this->$option) ? new Reference($this->$option) : $this->$option; + } + } + return (new Definition(is_string($this->lazy) ? $this->lazy : ($type ?: Database::class))) ->setFactory($value) ->setArguments([ $this->database ?? sprintf('%%%s.default_database%%', $this->serviceId), - $this->options, + $options, ]) ->setLazy($this->lazy); } diff --git a/tests/Unit/Attribute/AttributeTestCase.php b/tests/Unit/Attribute/AttributeTestCase.php new file mode 100644 index 0000000..98fa3bf --- /dev/null +++ b/tests/Unit/Attribute/AttributeTestCase.php @@ -0,0 +1,72 @@ + $codec, + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(0), + 'readConcern' => new ReadConcern('majority'), + 'readPreference' => new ReadPreference('primary'), + ]; + + foreach ($options as $option => $value) { + yield sprintf('%s option: null', $option) => [ + 'attributeArguments' => [$option => null], + 'expectedOptions' => [], + ]; + + yield sprintf('%s option: instance', $option) => [ + 'attributeArguments' => [$option => $value], + 'expectedOptions' => [$option => $value], + ]; + + yield sprintf('%s option: reference', $option) => [ + 'attributeArguments' => [$option => sprintf('%s_service', $option)], + 'expectedOptions' => [$option => new Reference(sprintf('%s_service', $option))], + ]; + } + } +} diff --git a/tests/Unit/Attribute/AutowireCollectionTest.php b/tests/Unit/Attribute/AutowireCollectionTest.php index 8c82588..006a73d 100644 --- a/tests/Unit/Attribute/AutowireCollectionTest.php +++ b/tests/Unit/Attribute/AutowireCollectionTest.php @@ -20,21 +20,16 @@ namespace MongoDB\Bundle\Tests\Unit\Attribute; -use MongoDB\BSON\Document; use MongoDB\Bundle\Attribute\AutowireCollection; use MongoDB\Client; -use MongoDB\Codec\DecodeIfSupported; -use MongoDB\Codec\DocumentCodec; -use MongoDB\Codec\EncodeIfSupported; use MongoDB\Collection; -use PHPUnit\Framework\TestCase; use ReflectionParameter; use Symfony\Component\DependencyInjection\Reference; use function sprintf; /** @covers \MongoDB\Bundle\Attribute\AutowireCollection */ -final class AutowireCollectionTest extends TestCase +final class AutowireCollectionTest extends AttributeTestCase { public function testMinimal(): void { @@ -65,7 +60,6 @@ public function testCollection(): void collection: 'test', database: 'mydb', client: 'default', - options: ['foo' => 'bar'], ); $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); @@ -84,7 +78,7 @@ static function (Collection $collection): void { $this->assertEquals($autowire->value, $definition->getFactory()); $this->assertSame('mydb', $definition->getArgument(0)); $this->assertSame('test', $definition->getArgument(1)); - $this->assertSame(['foo' => 'bar'], $definition->getArgument(2)); + $this->assertSame([], $definition->getArgument(2)); } public function testWithoutCollection(): void @@ -92,7 +86,6 @@ public function testWithoutCollection(): void $autowire = new AutowireCollection( database: 'mydb', client: 'default', - options: ['foo' => 'bar'], ); $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); @@ -111,73 +104,18 @@ static function (Collection $priceReports): void { $this->assertEquals($autowire->value, $definition->getFactory()); $this->assertSame('mydb', $definition->getArgument(0)); $this->assertSame('priceReports', $definition->getArgument(1)); - $this->assertSame(['foo' => 'bar'], $definition->getArgument(2)); - } - - public function testWithCodecOption(): void - { - $autowire = new AutowireCollection( - database: 'mydb', - client: 'default', - codec: '@my_codec', - options: ['foo' => 'bar'], - ); - - $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); - - $definition = $autowire->buildDefinition( - value: $autowire->value, - type: Collection::class, - parameter: new ReflectionParameter( - static function (Collection $priceReports): void { - }, - 'priceReports', - ), - ); - - $this->assertSame(Collection::class, $definition->getClass()); - $this->assertEquals($autowire->value, $definition->getFactory()); - $this->assertSame('mydb', $definition->getArgument(0)); - $this->assertSame('priceReports', $definition->getArgument(1)); - $this->assertEquals(['foo' => 'bar', 'codec' => new Reference('my_codec')], $definition->getArgument(2)); + $this->assertSame([], $definition->getArgument(2)); } - public function testWithCodecInstanceParameter(): void + /** @dataProvider provideOptions */ + public function testWithOptions(array $attributeArguments, array $expectedOptions): void { - $codec = new class implements DocumentCodec { - use DecodeIfSupported; - use EncodeIfSupported; - - public function canDecode($value): bool - { - return $value instanceof Document; - } - - public function canEncode($value): bool - { - return $value instanceof Document; - } - - public function decode($value): Document - { - return $value; - } - - public function encode($value): Document - { - return $value; - } - }; - $autowire = new AutowireCollection( + ...$attributeArguments, database: 'mydb', client: 'default', - codec: $codec, - options: ['foo' => 'bar'], ); - $this->assertEquals([new Reference('mongodb.client.default'), 'selectCollection'], $autowire->value); - $definition = $autowire->buildDefinition( value: $autowire->value, type: Collection::class, @@ -188,10 +126,6 @@ static function (Collection $priceReports): void { ), ); - $this->assertSame(Collection::class, $definition->getClass()); - $this->assertEquals($autowire->value, $definition->getFactory()); - $this->assertSame('mydb', $definition->getArgument(0)); - $this->assertSame('priceReports', $definition->getArgument(1)); - $this->assertEquals(['foo' => 'bar', 'codec' => $codec], $definition->getArgument(2)); + $this->assertEquals($expectedOptions, $definition->getArgument(2)); } } diff --git a/tests/Unit/Attribute/AutowireDatabaseTest.php b/tests/Unit/Attribute/AutowireDatabaseTest.php index 2f097e2..1958e20 100644 --- a/tests/Unit/Attribute/AutowireDatabaseTest.php +++ b/tests/Unit/Attribute/AutowireDatabaseTest.php @@ -22,13 +22,13 @@ use MongoDB\Bundle\Attribute\AutowireDatabase; use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Database; -use PHPUnit\Framework\TestCase; use ReflectionParameter; use Symfony\Component\DependencyInjection\Reference; /** @covers \MongoDB\Bundle\Attribute\AutowireDatabase */ -final class AutowireDatabaseTest extends TestCase +final class AutowireDatabaseTest extends AttributeTestCase { public function testMinimal(): void { @@ -56,7 +56,6 @@ public function testDatabase(): void $autowire = new AutowireDatabase( database: 'mydb', client: 'default', - options: ['foo' => 'bar'], ); $this->assertEquals([new Reference('mongodb.client.default'), 'selectDatabase'], $autowire->value); @@ -74,14 +73,13 @@ static function (Database $db): void { $this->assertSame(Database::class, $definition->getClass()); $this->assertEquals($autowire->value, $definition->getFactory()); $this->assertSame('mydb', $definition->getArgument(0)); - $this->assertSame(['foo' => 'bar'], $definition->getArgument(1)); + $this->assertSame([], $definition->getArgument(1)); } public function testWithoutDatabase(): void { $autowire = new AutowireDatabase( client: 'default', - options: ['foo' => 'bar'], ); $this->assertEquals([new Reference('mongodb.client.default'), 'selectDatabase'], $autowire->value); @@ -99,6 +97,27 @@ static function (Database $mydb): void { $this->assertSame(Database::class, $definition->getClass()); $this->assertEquals($autowire->value, $definition->getFactory()); $this->assertSame('%mongodb.client.default.default_database%', $definition->getArgument(0)); - $this->assertSame(['foo' => 'bar'], $definition->getArgument(1)); + $this->assertSame([], $definition->getArgument(1)); + } + + /** @dataProvider provideOptions */ + public function testWithOptions(array $attributeArguments, array $expectedOptions): void + { + $autowire = new AutowireDatabase( + ...$attributeArguments, + client: 'default', + ); + + $definition = $autowire->buildDefinition( + value: $autowire->value, + type: Collection::class, + parameter: new ReflectionParameter( + static function (Database $database): void { + }, + 'database', + ), + ); + + $this->assertEquals($expectedOptions, $definition->getArgument(1)); } } From 1d3bbf8e1808e42913f3701a8a2dbc230d3994ec Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Sat, 9 Dec 2023 14:32:29 +0100 Subject: [PATCH 6/6] Use parameter for type maps --- src/Attribute/AutowireCollection.php | 12 ++++++++++-- src/Attribute/AutowireDatabase.php | 12 ++++++++++-- tests/Unit/Attribute/AttributeTestCase.php | 17 ++++++++++++++++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/Attribute/AutowireCollection.php b/src/Attribute/AutowireCollection.php index fcce14e..6f99bb4 100644 --- a/src/Attribute/AutowireCollection.php +++ b/src/Attribute/AutowireCollection.php @@ -70,9 +70,17 @@ public function buildDefinition(mixed $value, ?string $type, ReflectionParameter { $options = []; foreach (['codec', 'typeMap', 'readPreference', 'writeConcern', 'readConcern'] as $option) { - if ($this->$option !== null) { - $options[$option] = is_string($this->$option) ? new Reference($this->$option) : $this->$option; + $optionValue = $this->$option; + if ($optionValue === null) { + continue; } + + // If a string was given, it may be a service ID or parameter. Handle it accordingly + if (is_string($optionValue)) { + $optionValue = $option === 'typeMap' ? sprintf('%%%s%%', $optionValue) : new Reference($optionValue); + } + + $options[$option] = $optionValue; } return (new Definition(is_string($this->lazy) ? $this->lazy : ($type ?: Collection::class))) diff --git a/src/Attribute/AutowireDatabase.php b/src/Attribute/AutowireDatabase.php index 0d64c30..0b9b56b 100644 --- a/src/Attribute/AutowireDatabase.php +++ b/src/Attribute/AutowireDatabase.php @@ -68,9 +68,17 @@ public function buildDefinition(mixed $value, ?string $type, ReflectionParameter { $options = []; foreach (['codec', 'typeMap', 'readPreference', 'writeConcern', 'readConcern'] as $option) { - if ($this->$option !== null) { - $options[$option] = is_string($this->$option) ? new Reference($this->$option) : $this->$option; + $optionValue = $this->$option; + if ($optionValue === null) { + continue; } + + // If a string was given, it may be a service ID or parameter. Handle it accordingly + if (is_string($optionValue)) { + $optionValue = $option === 'typeMap' ? sprintf('%%%s%%', $optionValue) : new Reference($optionValue); + } + + $options[$option] = $optionValue; } return (new Definition(is_string($this->lazy) ? $this->lazy : ($type ?: Database::class))) diff --git a/tests/Unit/Attribute/AttributeTestCase.php b/tests/Unit/Attribute/AttributeTestCase.php index 98fa3bf..e7ee39e 100644 --- a/tests/Unit/Attribute/AttributeTestCase.php +++ b/tests/Unit/Attribute/AttributeTestCase.php @@ -46,7 +46,6 @@ public function encode($value): Document $options = [ 'codec' => $codec, - 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(0), 'readConcern' => new ReadConcern('majority'), 'readPreference' => new ReadPreference('primary'), @@ -68,5 +67,21 @@ public function encode($value): Document 'expectedOptions' => [$option => new Reference(sprintf('%s_service', $option))], ]; } + + // Type map + yield 'typeMap option: null' => [ + 'attributeArguments' => ['typeMap' => null], + 'expectedOptions' => [], + ]; + + yield 'typeMap option: value' => [ + 'attributeArguments' => ['typeMap' => ['root' => 'bson']], + 'expectedOptions' => ['typeMap' => ['root' => 'bson']], + ]; + + yield 'typeMap option: parameter' => [ + 'attributeArguments' => ['typeMap' => 'default_typeMap'], + 'expectedOptions' => ['typeMap' => '%default_typeMap%'], + ]; } }