Skip to content

Allow specifying options in AutowireDatabase and AutowireCollection #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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,
) {}
}
```
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
14 changes: 13 additions & 1 deletion src/Attribute/AutowireCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
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;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

use function is_string;
use function ltrim;
use function sprintf;

/**
Expand All @@ -44,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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you pass a DocumentCodec instance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an example, but it's rather straightforward:

use MongoDB\Bundle\Attribute\AutowireCollection;
use MongoDB\Collection;
use MongoDB\Driver\ReadPreference;

class MyService
{
    public function __construct(
        #[AutowireCollection(codec: Codec::class, readPreference: new ReadPreference('secondary'))]
        private Collection $myCollection,
    ) {}
}

private readonly array $options = [],
bool|string $lazy = false,
) {
Expand All @@ -59,12 +62,21 @@ 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'], '@'));
}

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);
}
Expand Down
85 changes: 85 additions & 0 deletions tests/Unit/Attribute/AutowireCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -109,4 +113,85 @@ 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',
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));
}

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));
}
}