diff --git a/Dbal/RegexSchemaAssetFilter.php b/Dbal/RegexSchemaAssetFilter.php
new file mode 100644
index 000000000..08ab7d233
--- /dev/null
+++ b/Dbal/RegexSchemaAssetFilter.php
@@ -0,0 +1,25 @@
+filterExpression = $filterExpression;
+ }
+
+ public function __invoke($assetName): bool
+ {
+ if ($assetName instanceof AbstractAsset) {
+ $assetName = $assetName->getName();
+ }
+
+ return preg_match($this->filterExpression, $assetName);
+ }
+}
diff --git a/Dbal/SchemaAssetsFilterManager.php b/Dbal/SchemaAssetsFilterManager.php
new file mode 100644
index 000000000..b20782dfd
--- /dev/null
+++ b/Dbal/SchemaAssetsFilterManager.php
@@ -0,0 +1,30 @@
+schemaAssetFilters = $schemaAssetFilters;
+ }
+
+ public function __invoke($assetName): bool
+ {
+ foreach ($this->schemaAssetFilters as $schemaAssetFilter) {
+ if (false === $schemaAssetFilter($assetName)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/DependencyInjection/Compiler/DbalSchemaFilterPass.php b/DependencyInjection/Compiler/DbalSchemaFilterPass.php
new file mode 100644
index 000000000..97e8980a9
--- /dev/null
+++ b/DependencyInjection/Compiler/DbalSchemaFilterPass.php
@@ -0,0 +1,55 @@
+findTaggedServiceIds('doctrine.dbal.schema_filter');
+
+ if (count($filters) > 0 && !method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
+ throw new \LogicException('The doctrine.dbal.schema_filter tag is only supported when using doctrine/dbal 2.9 or higher.');
+ }
+
+ $connectionFilters = [];
+ foreach ($filters as $id => $tagAttributes) {
+ foreach ($tagAttributes as $attributes) {
+ $name = isset($attributes['connection']) ? $attributes['connection'] : $container->getParameter('doctrine.default_connection');
+
+ if (!isset($connectionFilters[$name])) {
+ $connectionFilters[$name] = [];
+ }
+
+ $connectionFilters[$name][] = new Reference($id);
+ }
+ }
+
+ foreach ($connectionFilters as $name => $references) {
+ $configurationId = sprintf('doctrine.dbal.%s_connection.configuration', $name);
+
+ if (!$container->hasDefinition($configurationId)) {
+ continue;
+ }
+
+ $definition = new ChildDefinition('doctrine.dbal.schema_asset_filter_manager');
+ $definition->setArgument(0, $references);
+
+ $id = sprintf('doctrine.dbal.%s_schema_asset_filter_manager', $name);
+ $container->setDefinition($id, $definition);
+ $container->findDefinition($configurationId)
+ ->addMethodCall('setSchemaAssetsFilter', [new Reference($id)]);
+ }
+ }
+}
diff --git a/DependencyInjection/DoctrineExtension.php b/DependencyInjection/DoctrineExtension.php
index 8c54f7bef..20371c8e4 100644
--- a/DependencyInjection/DoctrineExtension.php
+++ b/DependencyInjection/DoctrineExtension.php
@@ -2,6 +2,7 @@
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;
+use Doctrine\Bundle\DoctrineBundle\Dbal\RegexSchemaAssetFilter;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader;
@@ -150,7 +151,16 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
unset($connection['auto_commit']);
if (isset($connection['schema_filter']) && $connection['schema_filter']) {
- $configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
+ if (method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
+ $definition = new Definition(RegexSchemaAssetFilter::class, [
+ $connection['schema_filter']
+ ]);
+ $definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
+ $container->setDefinition(sprintf('doctrine.dbal.%s_regex_schema_filter', $name), $definition);
+ } else {
+ // backwards compatibility with dbal < 2.9
+ $configuration->addMethodCall('setFilterSchemaAssetsExpression', [$connection['schema_filter']]);
+ }
}
unset($connection['schema_filter']);
diff --git a/Resources/config/dbal.xml b/Resources/config/dbal.xml
index a94e91755..d0f2a868b 100644
--- a/Resources/config/dbal.xml
+++ b/Resources/config/dbal.xml
@@ -70,6 +70,10 @@
+
+
+
+
diff --git a/Tests/Dbal/RegexSchemaAssetFilterTest.php b/Tests/Dbal/RegexSchemaAssetFilterTest.php
new file mode 100644
index 000000000..14c77fa5c
--- /dev/null
+++ b/Tests/Dbal/RegexSchemaAssetFilterTest.php
@@ -0,0 +1,17 @@
+assertTrue($filter('do_not_t_ignore_me'));
+ $this->assertFalse($filter('t_ignore_me'));
+ }
+}
\ No newline at end of file
diff --git a/Tests/Dbal/SchemaAssetsFilterManagerTest.php b/Tests/Dbal/SchemaAssetsFilterManagerTest.php
new file mode 100644
index 000000000..0c0609583
--- /dev/null
+++ b/Tests/Dbal/SchemaAssetsFilterManagerTest.php
@@ -0,0 +1,23 @@
+assertSame(
+ ['do_not_filter'],
+ array_values(array_filter($tables, $manager))
+ );
+ }
+}
\ No newline at end of file
diff --git a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
index dae83e586..89c72f693 100644
--- a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
+++ b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
@@ -2,8 +2,11 @@
namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection;
+use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension;
+use Doctrine\DBAL\Configuration;
+use Doctrine\DBAL\Schema\AbstractAsset;
use Doctrine\ORM\EntityManager;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass;
@@ -761,10 +764,60 @@ public function testDbalOracleInstancename()
public function testDbalSchemaFilter()
{
+ if (method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
+ $this->markTestSkipped('Test only applies to doctrine/dbal 2.8 or lower');
+ }
+
$container = $this->loadContainer('dbal_schema_filter');
- $definition = $container->getDefinition('doctrine.dbal.default_connection.configuration');
- $this->assertDICDefinitionMethodCallOnce($definition, 'setFilterSchemaAssetsExpression', ['^sf2_']);
+ $definition = $container->getDefinition('doctrine.dbal.connection1_connection.configuration');
+ $this->assertDICDefinitionMethodCallOnce($definition, 'setFilterSchemaAssetsExpression', ['~^(?!t_)~']);
+ }
+
+ public function testDbalSchemaFilterNewConfig()
+ {
+ if (!method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
+ $this->markTestSkipped('Test requires doctrine/dbal 2.9 or higher');
+ }
+
+ $container = $this->getContainer([]);
+ $loader = new DoctrineExtension();
+ $container->registerExtension($loader);
+ $container->addCompilerPass(new DbalSchemaFilterPass());
+
+ // ignore table1 table on "default" connection
+ $container->register('dummy_filter1', DummySchemaAssetsFilter::class)
+ ->setArguments(['table1'])
+ ->addTag('doctrine.dbal.schema_filter');
+
+ // ignore table2 table on "connection2" connection
+ $container->register('dummy_filter2', DummySchemaAssetsFilter::class)
+ ->setArguments(['table2'])
+ ->addTag('doctrine.dbal.schema_filter', ['connection' => 'connection2']);
+
+ $this->loadFromFile($container, 'dbal_schema_filter');
+
+ $assetNames = ['table1', 'table2', 'table3', 't_ignored'];
+ $expectedConnectionAssets = [
+ // ignores table1 + schema_filter applies
+ 'connection1' => ['table2', 'table3'],
+ // ignores table2, no schema_filter applies
+ 'connection2' => ['table1', 'table3', 't_ignored'],
+ // connection3 has no ignores, handled separately
+ ];
+
+ $this->compileContainer($container);
+
+ $getConfiguration = function(string $connectionName) use ($container): Configuration {
+ return $container->get(sprintf('doctrine.dbal.%s_connection', $connectionName))->getConfiguration();
+ };
+
+ foreach ($expectedConnectionAssets as $connectionName => $expectedTables) {
+ $connConfig = $getConfiguration($connectionName);
+ $this->assertSame($expectedTables, array_values(array_filter($assetNames, $connConfig->getSchemaAssetsFilter())), sprintf('Filtering for connection "%s"', $connectionName));
+ }
+
+ $this->assertNull($connConfig = $getConfiguration('connection3')->getSchemaAssetsFilter());
}
public function testEntityListenerResolver()
@@ -1048,3 +1101,22 @@ private function compileContainer(ContainerBuilder $container)
$container->compile();
}
}
+
+class DummySchemaAssetsFilter
+{
+ private $tableToIgnore;
+
+ public function __construct(string $tableToIgnore)
+ {
+ $this->tableToIgnore = $tableToIgnore;
+ }
+
+ public function __invoke($assetName): bool
+ {
+ if ($assetName instanceof AbstractAsset) {
+ $assetName = $assetName->getName();
+ }
+
+ return $assetName !== $this->tableToIgnore;
+ }
+}
diff --git a/Tests/DependencyInjection/Fixtures/config/xml/dbal_schema_filter.xml b/Tests/DependencyInjection/Fixtures/config/xml/dbal_schema_filter.xml
index 2a897bb84..55f433269 100644
--- a/Tests/DependencyInjection/Fixtures/config/xml/dbal_schema_filter.xml
+++ b/Tests/DependencyInjection/Fixtures/config/xml/dbal_schema_filter.xml
@@ -7,6 +7,10 @@
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
-
+
+
+
+
+
diff --git a/Tests/DependencyInjection/Fixtures/config/yml/dbal_schema_filter.yml b/Tests/DependencyInjection/Fixtures/config/yml/dbal_schema_filter.yml
index 8a94d8092..c557507a1 100644
--- a/Tests/DependencyInjection/Fixtures/config/yml/dbal_schema_filter.yml
+++ b/Tests/DependencyInjection/Fixtures/config/yml/dbal_schema_filter.yml
@@ -1,3 +1,8 @@
doctrine:
dbal:
- schema_filter: ^sf2_
+ default_connection: connection1
+ connections:
+ connection1:
+ schema_filter: ~^(?!t_)~
+ connection2: []
+ connection3: []