Skip to content

Commit 6c16298

Browse files
authored
Merge pull request #323 from goetas/service-definition
Lazy services and service factories
2 parents 0e1800a + 5435638 commit 6c16298

File tree

6 files changed

+113
-8
lines changed

6 files changed

+113
-8
lines changed

DependencyInjection/Configuration.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ public function getConfigTreeBuilder() : TreeBuilder
6868
->prototype('scalar')->end()
6969
->end()
7070

71+
->arrayNode('factories')
72+
->info('A set of callables to pass to the underlying doctrine/migrations library as services, allowing to change its behaviour.')
73+
->useAttributeAsKey('factory')
74+
->defaultValue([])
75+
->validate()
76+
->ifTrue(static function ($v) {
77+
return count(array_filter(array_keys($v), static function (string $doctrineService) : bool {
78+
return strpos($doctrineService, 'Doctrine\Migrations\\') !==0;
79+
}));
80+
})
81+
->thenInvalid('Valid callables for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.')
82+
->end()
83+
->prototype('scalar')->end()
84+
->end()
85+
7186
->arrayNode('storage')
7287
->addDefaultsIfNotSet()
7388
->info('Storage to use for migration status metadata.')

DependencyInjection/DoctrineMigrationsExtension.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use InvalidArgumentException;
1010
use RuntimeException;
1111
use Symfony\Component\Config\FileLocator;
12+
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
1213
use Symfony\Component\DependencyInjection\ContainerBuilder;
1314
use Symfony\Component\DependencyInjection\Definition;
1415
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -67,7 +68,11 @@ public function load(array $configs, ContainerBuilder $container) : void
6768
$diDefinition = $container->getDefinition('doctrine.migrations.dependency_factory');
6869

6970
foreach ($config['services'] as $doctrineId => $symfonyId) {
70-
$diDefinition->addMethodCall('setService', [$doctrineId, new Reference($symfonyId)]);
71+
$diDefinition->addMethodCall('setDefinition', [$doctrineId, new ServiceClosureArgument(new Reference($symfonyId))]);
72+
}
73+
74+
foreach ($config['factories'] as $doctrineId => $symfonyId) {
75+
$diDefinition->addMethodCall('setDefinition', [$doctrineId, new Reference($symfonyId)]);
7176
}
7277

7378
if (! isset($config['services'][MetadataStorage::class])) {

Resources/doc/index.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@ application:
8888
# Custom migration classes factory
8989
'Doctrine\Migrations\Version\MigrationFactory': ~
9090
91+
factories:
92+
# Custom migration sorting service id via callables (MyCallableFactory must be a callable)
93+
'Doctrine\Migrations\Version\Comparator': 'MyCallableFactory'
94+
95+
96+
97+
98+
- The ``services`` node allows you to provide custom services to the underlying ``DependencyFactory`` part
99+
of ``doctrine/migrations``.
100+
101+
- The node ``factories`` is similar to ``services``, with the difference that it accepts only callables.
102+
The provided callable must return the service to be passed to the ``DependencyFactory``.
103+
The callable will receive as first argument the ``DependencyFactory`` itself,
104+
allowing you to fetch other dependencies from the factory while instantiating your custom dependencies.
105+
91106
Usage
92107
-----
93108

Tests/DependencyInjection/DoctrineMigrationsExtensionTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Doctrine\Migrations\Version\Comparator;
1717
use Doctrine\Migrations\Version\Version;
1818
use Doctrine\ORM\EntityManager;
19+
use Exception;
1920
use InvalidArgumentException;
2021
use PHPUnit\Framework\TestCase;
2122
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -25,6 +26,7 @@
2526
use Symfony\Component\DependencyInjection\Definition;
2627
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
2728
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
29+
use Symfony\Component\DependencyInjection\Reference;
2830
use function assert;
2931
use function method_exists;
3032
use function sys_get_temp_dir;
@@ -175,6 +177,69 @@ public function compare(Version $a, Version $b) : int
175177
self::assertSame($sorter, $di->getVersionComparator());
176178
}
177179

180+
public function testServicesAreLazy() : void
181+
{
182+
$config = [
183+
'services' => [Comparator::class => 'my_sorter'],
184+
];
185+
$container = $this->getContainer($config);
186+
187+
$conn = $this->createMock(Connection::class);
188+
$container->set('doctrine.dbal.default_connection', $conn);
189+
190+
$sorterFactory = new class() {
191+
public function __invoke() : void
192+
{
193+
throw new Exception('This method should not be invoked.');
194+
}
195+
};
196+
$container->set('my_sorter_factory', $sorterFactory);
197+
198+
$sorterDefinition = new Definition(Comparator::class);
199+
$sorterDefinition->setFactory(new Reference('my_sorter_factory'));
200+
$container->setDefinition('my_sorter', $sorterDefinition);
201+
202+
$container->compile();
203+
204+
$di = $container->get('doctrine.migrations.dependency_factory');
205+
self::assertInstanceOf(DependencyFactory::class, $di);
206+
}
207+
208+
public function testServiceFactory() : void
209+
{
210+
$mockComparator = $this->createMock(Comparator::class);
211+
$config = [
212+
'factories' => [Comparator::class => 'my_sorter'],
213+
];
214+
215+
$container = $this->getContainer($config);
216+
217+
$conn = $this->createMock(Connection::class);
218+
$container->set('doctrine.dbal.default_connection', $conn);
219+
220+
$sorterFactory = new class($mockComparator) {
221+
/** @var Comparator */
222+
private $comparator;
223+
224+
public function __construct(Comparator $comparator)
225+
{
226+
$this->comparator = $comparator;
227+
}
228+
229+
public function __invoke(DependencyFactory $di) : Comparator
230+
{
231+
return $this->comparator;
232+
}
233+
};
234+
$container->set('my_sorter', $sorterFactory);
235+
236+
$container->compile();
237+
238+
$di = $container->get('doctrine.migrations.dependency_factory');
239+
self::assertInstanceOf(DependencyFactory::class, $di);
240+
self::assertSame($mockComparator, $di->getVersionComparator());
241+
}
242+
178243
public function testCustomConnection() : void
179244
{
180245
$config = [

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@
4343
"autoload-dev": {
4444
"psr-4": { "Doctrine\\Bundle\\MigrationsBundle\\Tests\\": "Tests" }
4545
},
46+
"config": {
47+
"platform": {
48+
"php": "7.2.5"
49+
}
50+
},
4651
"extra": {
4752
"branch-alias": {
4853
"dev-master": "3.0.x-dev"

composer.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)