Skip to content

Commit 0185170

Browse files
authored
Merge pull request #368 from doctrine/container-aware-migration-factory
Provide container aware migration factory as default
2 parents c08a1d0 + 9d453c8 commit 0185170

File tree

10 files changed

+132
-8
lines changed

10 files changed

+132
-8
lines changed

.github/workflows/continuous-integration.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@ jobs:
2525
- "7.2"
2626
- "7.3"
2727
- "7.4"
28+
- "8.0"
2829
deps:
2930
- "normal"
3031
include:
3132
- deps: "low"
3233
php-version: "7.2"
33-
- deps: "dev"
34-
php-version: "8.0"
3534

3635
steps:
3736
- name: "Checkout"

DependencyInjection/CompilerPass/ConfigureDependencyFactoryPass.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Symfony\Component\DependencyInjection\ContainerBuilder;
1010
use Symfony\Component\DependencyInjection\Reference;
1111

12+
use function assert;
13+
use function is_string;
1214
use function sprintf;
1315

1416
class ConfigureDependencyFactoryPass implements CompilerPassInterface
@@ -18,6 +20,7 @@ public function process(ContainerBuilder $container): void
1820
$preferredEm = $container->getParameter('doctrine.migrations.preferred_em');
1921
$diDefinition = $container->getDefinition('doctrine.migrations.dependency_factory');
2022

23+
assert(is_string($preferredEm) || $preferredEm === null);
2124
$emID = sprintf('doctrine.orm.%s_entity_manager', $preferredEm ?? 'default');
2225

2326
if ($container->has($emID)) {
@@ -28,7 +31,8 @@ public function process(ContainerBuilder $container): void
2831
$diDefinition->setArgument(1, new Reference('doctrine.migrations.em_loader'));
2932
} else {
3033
$preferredConnection = $container->getParameter('doctrine.migrations.preferred_connection');
31-
$connectionId = sprintf('doctrine.dbal.%s_connection', $preferredConnection ?? 'default');
34+
assert(is_string($preferredConnection) || $preferredConnection === null);
35+
$connectionId = sprintf('doctrine.dbal.%s_connection', $preferredConnection ?? 'default');
3236
$container->getDefinition('doctrine.migrations.connection_loader')
3337
->setArgument(0, new Reference($connectionId));
3438

DependencyInjection/Configuration.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ public function getConfigTreeBuilder(): TreeBuilder
5959
->useAttributeAsKey('service')
6060
->defaultValue([])
6161
->validate()
62-
->ifTrue(static function ($v) {
62+
->ifTrue(static function ($v): bool {
6363
return count(array_filter(array_keys($v), static function (string $doctrineService): bool {
6464
return strpos($doctrineService, 'Doctrine\Migrations\\') !== 0;
65-
}));
65+
})) !== 0;
6666
})
6767
->thenInvalid('Valid services for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.')
6868
->end()
@@ -74,10 +74,10 @@ public function getConfigTreeBuilder(): TreeBuilder
7474
->useAttributeAsKey('factory')
7575
->defaultValue([])
7676
->validate()
77-
->ifTrue(static function ($v) {
77+
->ifTrue(static function ($v): bool {
7878
return count(array_filter(array_keys($v), static function (string $doctrineService): bool {
7979
return strpos($doctrineService, 'Doctrine\Migrations\\') !== 0;
80-
}));
80+
})) !== 0;
8181
})
8282
->thenInvalid('Valid callables for the DoctrineMigrationsBundle must be in the "Doctrine\Migrations" namespace.')
8383
->end()

DependencyInjection/DoctrineMigrationsExtension.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Doctrine\Migrations\Metadata\Storage\MetadataStorage;
88
use Doctrine\Migrations\Metadata\Storage\TableMetadataStorageConfiguration;
9+
use Doctrine\Migrations\Version\MigrationFactory;
910
use InvalidArgumentException;
1011
use RuntimeException;
1112
use Symfony\Component\Config\FileLocator;
@@ -17,8 +18,10 @@
1718
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
1819

1920
use function array_keys;
21+
use function assert;
2022
use function explode;
2123
use function implode;
24+
use function is_array;
2225
use function sprintf;
2326
use function strlen;
2427
use function substr;
@@ -70,6 +73,10 @@ public function load(array $configs, ContainerBuilder $container): void
7073

7174
$diDefinition = $container->getDefinition('doctrine.migrations.dependency_factory');
7275

76+
if (! isset($config['services'][MigrationFactory::class])) {
77+
$config['services'][MigrationFactory::class] = 'doctrine.migrations.migrations_factory';
78+
}
79+
7380
foreach ($config['services'] as $doctrineId => $symfonyId) {
7481
$diDefinition->addMethodCall('setDefinition', [$doctrineId, new ServiceClosureArgument(new Reference($symfonyId))]);
7582
}
@@ -136,6 +143,8 @@ private function getBundlePath(string $bundleName, ContainerBuilder $container):
136143
{
137144
$bundleMetadata = $container->getParameter('kernel.bundles_metadata');
138145

146+
assert(is_array($bundleMetadata));
147+
139148
if (! isset($bundleMetadata[$bundleName])) {
140149
throw new RuntimeException(sprintf(
141150
'The bundle "%s" has not been registered, available bundles: %s',
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Bundle\MigrationsBundle\MigrationsFactory;
6+
7+
use Doctrine\Migrations\AbstractMigration;
8+
use Doctrine\Migrations\Version\MigrationFactory;
9+
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
12+
class ContainerAwareMigrationFactory implements MigrationFactory
13+
{
14+
/**
15+
* @var ContainerInterface
16+
*/
17+
private $container;
18+
19+
/**
20+
* @var MigrationFactory
21+
*/
22+
private $migrationFactory;
23+
24+
public function __construct(MigrationFactory $migrationFactory, ContainerInterface $container)
25+
{
26+
$this->container = $container;
27+
$this->migrationFactory = $migrationFactory;
28+
}
29+
30+
public function createVersion(string $migrationClassName): AbstractMigration
31+
{
32+
$migration = $this->migrationFactory->createVersion($migrationClassName);
33+
34+
if ($migration instanceof ContainerAwareInterface) {
35+
$migration->setContainer($this->container);
36+
}
37+
38+
return $migration;
39+
}
40+
}

Resources/config/services.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
<service id="doctrine.migrations.configuration" class="Doctrine\Migrations\Configuration\Configuration" public="false">
2929
</service>
3030

31+
<service id="doctrine.migrations.migrations_factory" class="Doctrine\Migrations\Version\MigrationFactory">
32+
<factory service="doctrine.migrations.dependency_factory" method="getMigrationFactory"/>
33+
</service>
34+
35+
<service id="doctrine.migrations.container_aware_migrations_factory"
36+
class="Doctrine\Bundle\MigrationsBundle\MigrationsFactory\ContainerAwareMigrationFactory"
37+
decorates="doctrine.migrations.migrations_factory"
38+
>
39+
<argument id="doctrine.migrations.container_aware_migrations_factory.inner" type="service"/>
40+
<argument id="service_container" type="service"/>
41+
</service>
42+
3143
<service id="doctrine_migrations.diff_command" class="Doctrine\Migrations\Tools\Console\Command\DiffCommand">
3244

3345
<argument type="service" id="doctrine.migrations.dependency_factory"/>

Resources/doc/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ Here is an example on how to inject the service container into your migrations:
290290
}
291291
292292
293+
.. tip::
294+
295+
If your migration class implements the interface ``Symfony\Component\DependencyInjection\ContainerAwareInterface``
296+
this bundle will automatically inject the default symfony container into your migration class
297+
(this because the ``MigrationFactoryDecorator`` shown in this example is the default migration factory used by this bundle).
298+
299+
293300
Generating Migrations Automatically
294301
-----------------------------------
295302

Tests/DependencyInjection/DoctrineMigrationsExtensionTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\Bundle\MigrationsBundle\DependencyInjection\DoctrineMigrationsExtension;
88
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
99
use Doctrine\Bundle\MigrationsBundle\Tests\Fixtures\CustomEntityManager;
10+
use Doctrine\Bundle\MigrationsBundle\Tests\Fixtures\Migrations\Migration001;
1011
use Doctrine\Bundle\MigrationsBundle\Tests\Fixtures\TestBundle\TestBundle;
1112
use Doctrine\DBAL\Connection;
1213
use Doctrine\Migrations\Configuration\Configuration;
@@ -179,6 +180,27 @@ public function compare(Version $a, Version $b): int
179180
self::assertSame($sorter, $di->getVersionComparator());
180181
}
181182

183+
public function testContainerAwareMigrations(): void
184+
{
185+
$config = [
186+
'migrations_paths' => ['DoctrineMigrationsTest' => 'a'],
187+
];
188+
$container = $this->getContainer($config);
189+
190+
$conn = $this->createMock(Connection::class);
191+
$container->set('doctrine.dbal.default_connection', $conn);
192+
193+
$container->compile();
194+
195+
$di = $container->get('doctrine.migrations.dependency_factory');
196+
self::assertInstanceOf(DependencyFactory::class, $di);
197+
198+
$migration = $di->getMigrationFactory()->createVersion(Migration001::class);
199+
200+
self::assertInstanceOf(Migration001::class, $migration);
201+
self::assertSame($container, $migration->getContainer());
202+
}
203+
182204
public function testServicesAreLazy(): void
183205
{
184206
$config = [
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Bundle\MigrationsBundle\Tests\Fixtures\Migrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
12+
class Migration001 extends AbstractMigration implements ContainerAwareInterface
13+
{
14+
/** @var ContainerInterface */
15+
private $container;
16+
17+
public function up(Schema $schema): void
18+
{
19+
// TODO: Implement up() method.
20+
}
21+
22+
public function setContainer(?ContainerInterface $container = null): void
23+
{
24+
$this->container = $container;
25+
}
26+
27+
public function getContainer(): ?ContainerInterface
28+
{
29+
return $this->container;
30+
}
31+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"php": "^7.2|^8.0",
2424
"symfony/framework-bundle": "~3.4|~4.0|~5.0",
2525
"doctrine/doctrine-bundle": "~1.0|~2.0",
26-
"doctrine/migrations": "~3.0"
26+
"doctrine/migrations": "^3.0.3"
2727
},
2828
"require-dev": {
2929
"phpunit/phpunit": "^7.0|^8.0|^9.0",

0 commit comments

Comments
 (0)