Skip to content

Commit

Permalink
Removing deprecated call to Configuration::setSchemaAssetsFilter()
Browse files Browse the repository at this point in the history
... and making the schema filter stuff a "plugin" system, which is the
intention of the new way it's handled in doctrine/dbal
  • Loading branch information
weaverryan committed Mar 20, 2019
1 parent 00b14f4 commit f724b13
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 4 deletions.
24 changes: 24 additions & 0 deletions Dbal/RegexSchemaAssetFilter.php
@@ -0,0 +1,24 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Dbal;

use Doctrine\DBAL\Schema\AbstractAsset;

class RegexSchemaAssetFilter implements SchemaAssetFilterInterface
{
private $filterExpression;

public function __construct(string $filterExpression)
{
$this->filterExpression = $filterExpression;
}

public function shouldIncludeAsset($assetName): bool
{
if ($assetName instanceof AbstractAsset) {
$assetName = $assetName->getName();
}

return preg_match($this->filterExpression, $assetName);
}
}
17 changes: 17 additions & 0 deletions Dbal/SchemaAssetFilterInterface.php
@@ -0,0 +1,17 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Dbal;

/**
* Classes consumed by SchemaAssetsFilterManager to filter DBAL assets.
*/
interface SchemaAssetFilterInterface
{
/**
* Returning false will filter the asset out.
*
* @param $assetName
* @return bool
*/
public function shouldIncludeAsset($assetName): bool;
}
30 changes: 30 additions & 0 deletions Dbal/SchemaAssetsFilterManager.php
@@ -0,0 +1,30 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Dbal;

/**
* Manages schema filters passed to Connection::setSchemaAssetsFilter()
*/
class SchemaAssetsFilterManager
{
private $schemaAssetFilters;

/**
* @param SchemaAssetFilterInterface[] $schemaAssetFilters
*/
public function __construct(array $schemaAssetFilters)
{
$this->schemaAssetFilters = $schemaAssetFilters;
}

public function __invoke($assetName): bool
{
foreach ($this->schemaAssetFilters as $schemaAssetFilter) {
if (false === $schemaAssetFilter->shouldIncludeAsset($assetName)) {
return false;
}
}

return true;
}
}
56 changes: 56 additions & 0 deletions DependencyInjection/Compiler/DbalSchemaFilterPass.php
@@ -0,0 +1,56 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

/**
* Processes the doctrine.dbal.schema_filter
*/
class DbalSchemaFilterPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
// feature require doctrine/dbal 2.9 or higher
if (!method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
return;
}

$filters = $container->findTaggedServiceIds('doctrine.dbal.schema_filter');

$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)]);
}
}
}
12 changes: 11 additions & 1 deletion DependencyInjection/DoctrineExtension.php
Expand Up @@ -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;
Expand Down Expand Up @@ -151,7 +152,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']);
Expand Down
4 changes: 4 additions & 0 deletions Resources/config/dbal.xml
Expand Up @@ -70,6 +70,10 @@
<tag name="twig.extension" />
</service>

<service id="doctrine.dbal.schema_asset_filter_manager" class="Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetsFilterManager" public="false" abstract="true">
<!-- schema assets filters -->
</service>

<!-- commands -->
<service id="doctrine.database_create_command" class="Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand">
<argument type="service" id="doctrine" />
Expand Down
17 changes: 17 additions & 0 deletions Tests/Dbal/RegexSchemaAssetFilterTest.php
@@ -0,0 +1,17 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Tests\Dbal;

use Doctrine\Bundle\DoctrineBundle\Dbal\RegexSchemaAssetFilter;
use PHPUnit\Framework\TestCase;

class RegexSchemaAssetFilterTest extends TestCase
{
public function testShouldIncludeAsset()
{
$filter = new RegexSchemaAssetFilter('~^(?!t_)~');

$this->assertTrue($filter->shouldIncludeAsset('do_not_t_ignore_me'));
$this->assertFalse($filter->shouldIncludeAsset('t_ignore_me'));
}
}
23 changes: 23 additions & 0 deletions Tests/Dbal/SchemaAssetsFilterManagerTest.php
@@ -0,0 +1,23 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Tests\Dbal;

use Doctrine\Bundle\DoctrineBundle\Dbal\RegexSchemaAssetFilter;
use Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetsFilterManager;
use PHPUnit\Framework\TestCase;

class SchemaAssetsFilterManagerTest extends TestCase
{
public function testInvoke()
{
$filterA = new RegexSchemaAssetFilter('~^(?!t_)~');
$filterB = new RegexSchemaAssetFilter('~^(?!s_)~');

$manager = new SchemaAssetsFilterManager([$filterA, $filterB]);
$tables = ['do_not_filter', 't_filter_me', 's_filter_me_too'];
$this->assertSame(
['do_not_filter'],
array_values(array_filter($tables, $manager))
);
}
}
75 changes: 74 additions & 1 deletion Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
Expand Up @@ -2,8 +2,12 @@

namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection;

use Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetFilterInterface;
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;
Expand Down Expand Up @@ -761,10 +765,60 @@ public function testDbalOracleInstancename()

public function testDbalSchemaFilter()
{
if (method_exists(\Doctrine\DBAL\Configuration::class, 'setSchemaAssetsFilter')) {
$this->markTestSkipped('Test only applies to doctrine/dbal 2.9 or lower');
}

$container = $this->loadContainer('dbal_schema_filter');

$definition = $container->getDefinition('doctrine.dbal.default_connection.configuration');
$this->assertDICDefinitionMethodCallOnce($definition, 'setFilterSchemaAssetsExpression', ['^sf2_']);
$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()
Expand Down Expand Up @@ -1048,3 +1102,22 @@ private function compileContainer(ContainerBuilder $container)
$container->compile();
}
}

class DummySchemaAssetsFilter implements SchemaAssetFilterInterface
{
private $tableToIgnore;

public function __construct(string $tableToIgnore)
{
$this->tableToIgnore = $tableToIgnore;
}

public function shouldIncludeAsset($assetName): bool
{
if ($assetName instanceof AbstractAsset) {
$assetName = $assetName->getName();
}

return $assetName !== $this->tableToIgnore;
}
}
Expand Up @@ -7,6 +7,10 @@
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">

<config>
<dbal schema-filter="^sf2_" />
<dbal default-connection="connection1">
<connection name="connection1" schema-filter="~^(?!t_)~" />
<connection name="connection2" />
<connection name="connection3" />
</dbal>
</config>
</srv:container>
@@ -1,3 +1,8 @@
doctrine:
dbal:
schema_filter: ^sf2_
default_connection: connection1
connections:
connection1:
schema_filter: ~^(?!t_)~
connection2: []
connection3: []

0 comments on commit f724b13

Please sign in to comment.