Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add support for --complete#526

Merged
greg0ire merged 1 commit into
doctrine:3.4.xfrom
greg0ire:complete-compat
Jan 16, 2025
Merged

Add support for --complete#526
greg0ire merged 1 commit into
doctrine:3.4.xfrom
greg0ire:complete-compat

Conversation

@greg0ire
Copy link
Copy Markdown
Member

@greg0ire greg0ire commented Mar 14, 2024

When using orm:schema-tool:update together with --complete, all assets not described by the current metadata are dropped.

This is an issue when using doctrine/migrations, because not using that option is deprecated, and because when it is used, the table that holds the migrations is dropped as well since it is not described by ORM metadata.

A solution to that is configuring an asset filter that filters out the metadata table except when running commands inside the migrations namespace.

How can I test this?

composer config repositories.greg0ire vcs https://github.com/greg0ire/DoctrineMigrationsBundle
composer require doctrine/doctrine-migrations-bundle "dev-complete-compat as 3.3.0"

<service id="doctrine_migrations.schema_filter_listener" class="Doctrine\Migrations\EventListener\SchemaFilterListener">
<tag name="kernel.event_listener" event="console.command" method="onConsoleCommand" />
<tag name="doctrine.dbal.schema_filter" />
</service>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the part I am not sure about, because I have been enable to test it. I've tried accessing the schema filter manager definition in order to inspect it after the compiler pass, like this:

    public function testItRegistersTheSchemaFilterListener(): void
    {
        $container = $this->getContainer([]);

        $container->getDefinition('doctrine.dbal.schema_asset_filter_manager')->setPublic(true);
        $container->compile();

        $container->get('doctrine.dbal.schema_asset_filter_manager');
    }

That last line results in

Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The "doctrine.dbal.schema_asset_filter_manager" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.

Any ideas how to test this?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doctrine.dbal.schema_asset_filter_manager is defined as an abstract. The container builder removes unused and/or abstract definitions during its compilation causing the service to be hidden.

An example fix, not per se the wanted fix, could be:

$container
  ->getDefinition('doctrine.dbal.schema_asset_filter_manager')
  ->setAbstract(false)
  ->setArgument(0, [])
  ->setPublic(true);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK… I guess since it's abstract, I should try to retrieve a concrete definition inheriting from it. I inspected the container, and there does not seem to be any :(

@greg0ire greg0ire force-pushed the complete-compat branch 2 times, most recently from feca975 to 2cfbb72 Compare March 14, 2024 16:29
@greg0ire greg0ire changed the base branch from 3.3.x to 3.4.x March 14, 2024 16:32
@greg0ire greg0ire force-pushed the complete-compat branch 2 times, most recently from 8ad6f64 to 656c9ee Compare March 14, 2024 16:45
@greg0ire
Copy link
Copy Markdown
Member Author

Cc @FlyingDR :)

@christian-kolb
Copy link
Copy Markdown

Anything I can do to get this over the finish line? Would love to be able to remove the workaround we've got in our app 🙂

@greg0ire
Copy link
Copy Markdown
Member Author

You could test it on your application and confirm whether it works or not.

@christian-kolb
Copy link
Copy Markdown

@greg0ire Ok, I will. Are the conflicts which are mentioned by the PR going to be a stumbling block for it?

@christian-kolb
Copy link
Copy Markdown

@greg0ire Unfortunately it already fails on cache:clear:

 php bin/console cache:clear
[2025-01-10T15:06:13.071685+01:00] console.CRITICAL: Error thrown while running command "cache:clear". Message: "Class "Doctrine\Migrations\EventListener\SchemaFilterListener" not found" {"exception":"[object] (Error(code: 0): Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found at /var/www/html/api/var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:703)","command":"cache:clear","message":"Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found"} []
[2025-01-10T15:06:13.071685+01:00] console.CRITICAL: Error thrown while running command "cache:clear". Message: "Class "Doctrine\Migrations\EventListener\SchemaFilterListener" not found" {"exception":"[object] (Error(code: 0): Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found at /var/www/html/api/var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:703)","command":"cache:clear","message":"Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found"} []
[2025-01-10T15:06:13.072295+01:00] console.DEBUG: Command "cache:clear" exited with code "1" {"command":"cache:clear","code":1} []
[2025-01-10T15:06:13.072369+01:00] php.CRITICAL: Uncaught Error: Class "Doctrine\Migrations\EventListener\SchemaFilterListener" not found {"exception":"[object] (Error(code: 0): Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found at /var/www/html/api/var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:703)"} []
[2025-01-10T15:06:13.072369+01:00] php.CRITICAL: Uncaught Error: Class "Doctrine\Migrations\EventListener\SchemaFilterListener" not found {"exception":"[object] (Error(code: 0): Class \"Doctrine\\Migrations\\EventListener\\SchemaFilterListener\" not found at /var/www/html/api/var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:703)"} []
Symfony\Component\ErrorHandler\Error\ClassNotFoundError^ {#1415
  #message: """
    Attempted to load class "SchemaFilterListener" from namespace "Doctrine\Migrations\EventListener".\n
    Did you forget a "use" statement for "Doctrine\Bundle\MigrationsBundle\EventListener\SchemaFilterListener"?
    """
  #code: 0
  #file: "./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php"
  #line: 703
  trace: {
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:703 {
      ContainerXRKMlYx\App_KernelDevDebugContainer::getDoctrine_Dbal_DefaultConnectionService($container)^
      › $b->setSchemaManagerFactory(($container->privates['doctrine.dbal.legacy_schema_manager_factory'] ??= new \Doctrine\DBAL\Schema\LegacySchemaManagerFactory()));
      › $b->setSchemaAssetsFilter(new \Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetsFilterManager([new \Doctrine\Bundle\DoctrineBundle\Dbal\RegexSchemaAssetFilter('~^(?!(?:domain_messages)|(?:domain_snapshots)|(?:cache_items)|(?:projection_handler_event_lock))~'), ($container->privates['doctrine_migrations.schema_filter_listener'] ??= new \Doctrine\Migrations\EventListener\SchemaFilterListener())]));
      › $b->setMiddlewares([new \Doctrine\DBAL\Logging\Middleware(($container->privates['monolog.logger.doctrine'] ?? self::getMonolog_Logger_DoctrineService($container))), $c]);
    }
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:1379 { …}
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:1363 { …}
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:653 { …}
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:556 { …}
    ./var/cache/dev/ContainerXRKMlYx/getConsoleProfilerListenerService.php:23 { …}
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:506 { …}
    ./var/cache/dev/ContainerXRKMlYx/App_KernelDevDebugContainer.php:768 { …}
    ./vendor/symfony/event-dispatcher/EventDispatcher.php:221 { …}
    ./vendor/symfony/event-dispatcher/EventDispatcher.php:70 { …}
    ./vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:252 { …}
    ./vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:116 { …}
    ./vendor/symfony/console/Application.php:1044 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:123 { …}
    ./vendor/symfony/console/Application.php:316 { …}
    ./vendor/symfony/framework-bundle/Console/Application.php:77 { …}
    ./vendor/symfony/console/Application.php:167 { …}
    ./bin/console:38 { …}
  }
}

The service is mentioned in services.xml as Doctrine\Migrations\EventListener\SchemaFilterListener but the namespace in the class seems to be Doctrine\Bundle\MigrationsBundle\EventListener.

Fixing it to be the following seems to be necessary:

<service id="doctrine_migrations.schema_filter_listener" class="Doctrine\Bundle\MigrationsBundle\EventListener\SchemaFilterListener">

But even with this added, when running: php bin/console doctrine:schema:update --dump-sql it results in:

DROP TABLE migration_versions;

@christian-kolb
Copy link
Copy Markdown

@greg0ire Is there anything that needs to be configured for it to work?

Comment thread Resources/config/services.xml Outdated
@greg0ire
Copy link
Copy Markdown
Member Author

Ok, I will. Are the conflicts which are mentioned by the PR going to be a stumbling block for it?

No.

Thanks for your suggestion, I applied it. I will need to fix the conflicts for this to get merged though.

Is there anything that needs to be configured for it to work?

No, I don't think so…

Comment thread EventListener/SchemaFilterListener.php Outdated

if (! isset($config['services'][MetadataStorage::class])) {
if (isset($config['services'][MetadataStorage::class])) {
$container->removeDefinition('doctrine_migrations.schema_filter_listener');
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a user defines their own storage class, there is no telling what they are going to do… they might even not store it in a database. At this point they're one their own.

@christian-kolb
Copy link
Copy Markdown

@greg0ire Awesome, now it's working. Now I get what I should get:

[OK] Nothing to update - your database is already in sync with the current entity metadata.  

@greg0ire greg0ire marked this pull request as ready for review January 11, 2025 12:35
@greg0ire
Copy link
Copy Markdown
Member Author

Thanks for helping moving this forward! Let's get this reviewed now.

Comment thread src/EventListener/SchemaFilterListener.php Outdated
Comment on lines +25 to +32
public function testItFiltersNothingWhenDisabled(): void
{
$listener = new SchemaFilterListener('doctrine_migration_versions');
$listener->disable();

self::assertTrue($listener(new Table('doctrine_migration_versions')));
self::assertTrue($listener(new Table('some_other_table')));
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this use case. When do you need to disable it in userland by calling the public disable-method?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I suspect I might have written that one before writing the next use case. I'll remove it and make the method private, if it's needed in userland, people can make it public later.

When using orm:schema-tool:update together with --complete, all assets
not described by the current metadata is dropped.

This is an issue when using doctrine/migrations, because not using that
option is deprecated, and because when it is used, the table that holds
the migrations is dropped as well since it is not described by ORM
metadata.

A solution to that is configuring an asset filter that filters out the
metadata table except when running commands inside the migrations
namespace.
@greg0ire greg0ire merged commit 45d1b74 into doctrine:3.4.x Jan 16, 2025
@greg0ire greg0ire deleted the complete-compat branch January 16, 2025 20:19
@greg0ire greg0ire added this to the 3.4.0 milestone Jan 16, 2025
@kevinpapst
Copy link
Copy Markdown

kevinpapst commented Jan 18, 2025

EDIT: posted as new bug report in #586

if ($storageConfiguration['table_name'] !== null) {
if ($storageConfiguration['table_name'] === null) {
$filterDefinition->addArgument('doctrine_migration_versions');
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After implementing these changes, running the migration results in the following exception:
SQLSTATE[42P07]: Duplicate table: 7 ERROR: relation "doctrine_migration_versions" already exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants