Skip to content

Commit

Permalink
Merge pull request #1037 from nicolas-grekas/auto-ignore-sf-tables
Browse files Browse the repository at this point in the history
Filter out well-known Symfony tables from DBAL schema migrations
  • Loading branch information
alcaeus authored Oct 23, 2019
2 parents 4e58171 + e4ca3d2 commit 2d64753
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Dbal/BlacklistSchemaAssetFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Dbal;

use Doctrine\DBAL\Schema\AbstractAsset;
use function in_array;

class BlacklistSchemaAssetFilter
{
/** @var string[] */
private $blacklist;

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

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

return ! in_array($assetName, $this->blacklist, true);
}
}
65 changes: 65 additions & 0 deletions DependencyInjection/Compiler/WellKnownSchemaFilterPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;

use Doctrine\DBAL\Configuration;
use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
use Symfony\Component\Lock\Store\PdoStore;
use Symfony\Component\Messenger\Transport\Doctrine\Connection;

/**
* Blacklist tables used by well-known Symfony classes.
*/
class WellKnownSchemaFilterPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (! method_exists(Configuration::class, 'setSchemaAssetsFilter')) {
// only supported when using doctrine/dbal 2.9 or higher
return;
}

$blacklist = [];

foreach ($container->getDefinitions() as $definition) {
if ($definition->isAbstract() || $definition->isSynthetic()) {
continue;
}

switch ($definition->getClass()) {
case PdoAdapter::class:
$blacklist[] = $definition->getArguments()[3]['db_table'] ?? 'cache_items';
break;

case PdoSessionHandler::class:
$blacklist[] = $definition->getArguments()[1]['db_table'] ?? 'lock_keys';
break;

case PdoStore::class:
$blacklist[] = $definition->getArguments()[1]['db_table'] ?? 'sessions';
break;

case Connection::class:
$blacklist[] = $definition->getArguments()[0]['table_name'] ?? 'messenger_messages';
break;
}
}

if (! $blacklist) {
return;
}

$definition = $container->getDefinition('doctrine.dbal.well_known_schema_asset_filter');
$definition->replaceArgument(0, $blacklist);

foreach (array_keys($container->getParameter('doctrine.connections')) as $name) {
$definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]);
}
}
}
2 changes: 2 additions & 0 deletions DoctrineBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Proxy\Autoloader;
Expand Down Expand Up @@ -37,6 +38,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new DoctrineValidationPass('orm'));
$container->addCompilerPass(new EntityListenerPass());
$container->addCompilerPass(new ServiceRepositoryCompilerPass());
$container->addCompilerPass(new WellKnownSchemaFilterPass());
$container->addCompilerPass(new DbalSchemaFilterPass());
}

Expand Down
4 changes: 4 additions & 0 deletions Resources/config/dbal.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
<!-- schema assets filters -->
</service>

<service id="doctrine.dbal.well_known_schema_asset_filter" class="Doctrine\Bundle\DoctrineBundle\Dbal\BlacklistSchemaAssetFilter" public="false">
<argument type="collection" />
</service>

<!-- commands -->
<service id="doctrine.database_create_command" class="Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand">
<argument type="service" id="doctrine" />
Expand Down
64 changes: 64 additions & 0 deletions Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Schema\AbstractAsset;
Expand Down Expand Up @@ -804,6 +805,7 @@ public function testDbalSchemaFilterNewConfig()
$container = $this->getContainer([]);
$loader = new DoctrineExtension();
$container->registerExtension($loader);
$container->addCompilerPass(new WellKnownSchemaFilterPass());
$container->addCompilerPass(new DbalSchemaFilterPass());

// ignore table1 table on "default" connection
Expand Down Expand Up @@ -841,6 +843,68 @@ public function testDbalSchemaFilterNewConfig()
$this->assertNull($connConfig = $getConfiguration('connection3')->getSchemaAssetsFilter());
}

public function testWellKnownSchemaFilterDefaultTables()
{
if (! method_exists(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 WellKnownSchemaFilterPass());
$container->addCompilerPass(new DbalSchemaFilterPass());

$this->loadFromFile($container, 'well_known_schema_filter_default_tables');

$this->compileContainer($container);

$definition = $container->getDefinition('doctrine.dbal.well_known_schema_asset_filter');

$this->assertSame([['cache_items', 'lock_keys', 'sessions', 'messenger_messages']], $definition->getArguments());
$this->assertSame([['connection' => 'connection1'], ['connection' => 'connection2'], ['connection' => 'connection3']], $definition->getTag('doctrine.dbal.schema_filter'));

$definition = $container->getDefinition('doctrine.dbal.connection1_schema_asset_filter_manager');

$this->assertEquals([new Reference('doctrine.dbal.well_known_schema_asset_filter'), new Reference('doctrine.dbal.connection1_regex_schema_filter')], $definition->getArgument(0));

$filter = $container->get('well_known_filter');

$this->assertFalse($filter('sessions'));
$this->assertFalse($filter('cache_items'));
$this->assertFalse($filter('lock_keys'));
$this->assertFalse($filter('messenger_messages'));
$this->assertTrue($filter('anything_else'));
}

public function testWellKnownSchemaFilterOverriddenTables()
{
if (! method_exists(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 WellKnownSchemaFilterPass());
$container->addCompilerPass(new DbalSchemaFilterPass());

$this->loadFromFile($container, 'well_known_schema_filter_overridden_tables');

$this->compileContainer($container);

$filter = $container->get('well_known_filter');

$this->assertFalse($filter('app_session'));
$this->assertFalse($filter('app_cache'));
$this->assertFalse($filter('app_locks'));
$this->assertFalse($filter('app_messages'));
$this->assertTrue($filter('sessions'));
$this->assertTrue($filter('cache_items'));
$this->assertTrue($filter('lock_keys'));
$this->assertTrue($filter('messenger_messages'));
}

public function testEntityListenerResolver()
{
$container = $this->loadContainer('orm_entity_listener_resolver', ['YamlBundle'], new EntityListenerPass());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" ?>

<srv:container xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">

<config>
<dbal default-connection="connection1">
<connection name="connection1" schema-filter="~^(?!t_)~" />
<connection name="connection2" />
<connection name="connection3" />
</dbal>
</config>

<srv:services>
<srv:service id="well_known_filter" alias="doctrine.dbal.well_known_schema_asset_filter" public="true" />
<srv:service id="symfony.cache" class="Symfony\Component\Cache\Adapter\PdoAdapter" />
<srv:service id="symfony.session" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler" />
<srv:service id="symfony.lock" class="Symfony\Component\Lock\Store\PdoStore" />
<srv:service id="symfony.messenger" class="Symfony\Component\Messenger\Transport\Doctrine\Connection" />
</srv:services>
</srv:container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" ?>

<srv:container xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">

<config>
<dbal default-connection="connection1">
<connection name="connection1" schema-filter="~^(?!t_)~" />
<connection name="connection2" />
<connection name="connection3" />
</dbal>
</config>

<srv:services>
<srv:service id="well_known_filter" alias="doctrine.dbal.well_known_schema_asset_filter" public="true" />
<srv:service id="symfony.cache" class="Symfony\Component\Cache\Adapter\PdoAdapter">
<srv:argument />
<srv:argument />
<srv:argument />
<srv:argument type="collection">
<srv:argument key="db_table">app_cache</srv:argument>
</srv:argument>
</srv:service>
<srv:service id="symfony.session" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler">
<srv:argument />
<srv:argument type="collection">
<srv:argument key="db_table">app_session</srv:argument>
</srv:argument>
</srv:service>
<srv:service id="symfony.lock" class="Symfony\Component\Lock\Store\PdoStore">
<srv:argument />
<srv:argument type="collection">
<srv:argument key="db_table">app_locks</srv:argument>
</srv:argument>
</srv:service>
<srv:service id="symfony.messenger" class="Symfony\Component\Messenger\Transport\Doctrine\Connection">
<srv:argument type="collection">
<srv:argument key="table_name">app_messages</srv:argument>
</srv:argument>
</srv:service>
</srv:services>
</srv:container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
doctrine:
dbal:
default_connection: connection1
connections:
connection1:
schema_filter: ~^(?!t_)~
connection2: []
connection3: []

services:
well_known_filter:
alias: 'doctrine.dbal.well_known_schema_asset_filter'
public: true

symfony.cache:
class: 'Symfony\Component\Cache\Adapter\PdoAdapter'

symfony.session:
class: 'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler'

symfony.lock:
class: 'Symfony\Component\Lock\Store\PdoStore'

symfony.messenger:
class: 'Symfony\Component\Messenger\Transport\Doctrine\Connection'
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
doctrine:
dbal:
default_connection: connection1
connections:
connection1:
schema_filter: ~^(?!t_)~
connection2: []
connection3: []

services:
well_known_filter:
alias: 'doctrine.dbal.well_known_schema_asset_filter'
public: true

symfony.cache:
class: 'Symfony\Component\Cache\Adapter\PdoAdapter'
arguments:
- ~
- ~
- ~
- db_table: app_cache

symfony.session:
class: 'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler'
arguments:
- ~
- db_table: app_session

symfony.lock:
class: 'Symfony\Component\Lock\Store\PdoStore'
arguments:
- ~
- db_table: app_locks

symfony.messenger:
class: 'Symfony\Component\Messenger\Transport\Doctrine\Connection'
arguments:
- table_name: app_messages

0 comments on commit 2d64753

Please sign in to comment.