Skip to content

Commit

Permalink
Merge pull request #323 from goetas/service-definition
Browse files Browse the repository at this point in the history
Lazy services and service factories
  • Loading branch information
goetas authored May 11, 2020
2 parents 0e1800a + 5435638 commit 6c16298
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 8 deletions.
15 changes: 15 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ public function getConfigTreeBuilder() : TreeBuilder
->prototype('scalar')->end()
->end()

->arrayNode('factories')
->info('A set of callables to pass to the underlying doctrine/migrations library as services, allowing to change its behaviour.')
->useAttributeAsKey('factory')
->defaultValue([])
->validate()
->ifTrue(static function ($v) {
return count(array_filter(array_keys($v), static function (string $doctrineService) : bool {
return strpos($doctrineService, 'Doctrine\Migrations\\') !==0;
}));
})
->thenInvalid('Valid callables for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.')
->end()
->prototype('scalar')->end()
->end()

->arrayNode('storage')
->addDefaultsIfNotSet()
->info('Storage to use for migration status metadata.')
Expand Down
7 changes: 6 additions & 1 deletion DependencyInjection/DoctrineMigrationsExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
Expand Down Expand Up @@ -67,7 +68,11 @@ public function load(array $configs, ContainerBuilder $container) : void
$diDefinition = $container->getDefinition('doctrine.migrations.dependency_factory');

foreach ($config['services'] as $doctrineId => $symfonyId) {
$diDefinition->addMethodCall('setService', [$doctrineId, new Reference($symfonyId)]);
$diDefinition->addMethodCall('setDefinition', [$doctrineId, new ServiceClosureArgument(new Reference($symfonyId))]);
}

foreach ($config['factories'] as $doctrineId => $symfonyId) {
$diDefinition->addMethodCall('setDefinition', [$doctrineId, new Reference($symfonyId)]);
}

if (! isset($config['services'][MetadataStorage::class])) {
Expand Down
15 changes: 15 additions & 0 deletions Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ application:
# Custom migration classes factory
'Doctrine\Migrations\Version\MigrationFactory': ~
factories:
# Custom migration sorting service id via callables (MyCallableFactory must be a callable)
'Doctrine\Migrations\Version\Comparator': 'MyCallableFactory'
- The ``services`` node allows you to provide custom services to the underlying ``DependencyFactory`` part
of ``doctrine/migrations``.

- The node ``factories`` is similar to ``services``, with the difference that it accepts only callables.
The provided callable must return the service to be passed to the ``DependencyFactory``.
The callable will receive as first argument the ``DependencyFactory`` itself,
allowing you to fetch other dependencies from the factory while instantiating your custom dependencies.

Usage
-----

Expand Down
65 changes: 65 additions & 0 deletions Tests/DependencyInjection/DoctrineMigrationsExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Doctrine\Migrations\Version\Comparator;
use Doctrine\Migrations\Version\Version;
use Doctrine\ORM\EntityManager;
use Exception;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
Expand All @@ -25,6 +26,7 @@
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use function assert;
use function method_exists;
use function sys_get_temp_dir;
Expand Down Expand Up @@ -175,6 +177,69 @@ public function compare(Version $a, Version $b) : int
self::assertSame($sorter, $di->getVersionComparator());
}

public function testServicesAreLazy() : void
{
$config = [
'services' => [Comparator::class => 'my_sorter'],
];
$container = $this->getContainer($config);

$conn = $this->createMock(Connection::class);
$container->set('doctrine.dbal.default_connection', $conn);

$sorterFactory = new class() {
public function __invoke() : void
{
throw new Exception('This method should not be invoked.');
}
};
$container->set('my_sorter_factory', $sorterFactory);

$sorterDefinition = new Definition(Comparator::class);
$sorterDefinition->setFactory(new Reference('my_sorter_factory'));
$container->setDefinition('my_sorter', $sorterDefinition);

$container->compile();

$di = $container->get('doctrine.migrations.dependency_factory');
self::assertInstanceOf(DependencyFactory::class, $di);
}

public function testServiceFactory() : void
{
$mockComparator = $this->createMock(Comparator::class);
$config = [
'factories' => [Comparator::class => 'my_sorter'],
];

$container = $this->getContainer($config);

$conn = $this->createMock(Connection::class);
$container->set('doctrine.dbal.default_connection', $conn);

$sorterFactory = new class($mockComparator) {
/** @var Comparator */
private $comparator;

public function __construct(Comparator $comparator)
{
$this->comparator = $comparator;
}

public function __invoke(DependencyFactory $di) : Comparator
{
return $this->comparator;
}
};
$container->set('my_sorter', $sorterFactory);

$container->compile();

$di = $container->get('doctrine.migrations.dependency_factory');
self::assertInstanceOf(DependencyFactory::class, $di);
self::assertSame($mockComparator, $di->getVersionComparator());
}

public function testCustomConnection() : void
{
$config = [
Expand Down
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
"autoload-dev": {
"psr-4": { "Doctrine\\Bundle\\MigrationsBundle\\Tests\\": "Tests" }
},
"config": {
"platform": {
"php": "7.2.5"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
Expand Down
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6c16298

Please sign in to comment.