Validate schema command isn't handling entities with attributes #6889

Closed
opened 2026-01-22 15:40:45 +01:00 by admin · 3 comments
Owner

Originally created by @oojacoboo on GitHub (Dec 15, 2021).

Bug Report

Q A
BC Break no
Version 2.11.x-dev

Summary

I should start by saying that we're in the process of updating our entities to use attributes instead of annotations. This is going to be a gradual process. Therefore, we need to support both, attributes and annotations, until that migration has completed. To do this, we've implemented a custom Mapping\Driver, as seen below:

<?php

declare(strict_types = 1);

namespace Acme\Adapter\Doctrine\ORM\Mapping;

use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Psr\Log\LoggerInterface;


/**
 * Custom Mapping\Driver
 * Implements both the AttributeDriver and AnnotationDriver.  This allows for us
 * to use either attributes or annotations on our Doctrine entities.
 */
class Driver implements MappingDriver
{

    public function __construct(
        protected AnnotationDriver $annotationDriver,
        protected AttributeDriver $attributeDriver,
        protected LoggerInterface $logger,
        protected bool $debug,
    ) {
    }


    /** @inheritdoc */
    public function loadMetadataForClass($className, ClassMetadata $metadata): void
    {
        try {
            $this->attributeDriver->loadMetadataForClass($className, $metadata);
            $this->logEvent(sprintf('AttributeDriver utilized: %s', $className));
            return;
        } catch (MappingException $me) {
            // Class X is not a valid entity, so try the other driver
            if (!preg_match('/^Class(.)*$/', $me->getMessage())) { // meh
                $this->logEvent('different MappingException: ' . $me->getMessage());
                throw $me;
            }
        }

        $this->annotationDriver->loadMetadataForClass($className, $metadata);
        $this->logEvent(sprintf('AnnotationDriver utilized: %s', $className));
    }


    /** @inheritdoc */
    public function getAllClassNames(): array
    {
        return array_merge(
            $this->annotationDriver->getAllClassNames(),
            $this->attributeDriver->getAllClassNames(),
        );
    }


    /** @inheritdoc */
    public function isTransient($className): bool
    {
        return $this->attributeDriver->isTransient($className)
            || $this->annotationDriver->isTransient($className);
    }


    /** @inheritdoc */
    private function logEvent(string $message): void
    {
        if (!$this->debug) {
            return;
        }

        $this->logger->info($message);
    }
}

Current behavior

This, when being used by the EntityManager (registered as the MetadataDriverImpl), works fine with the orm:generate-proxies command. However, when running the orm:validate-schema, it's unable to validate, failing to find the class that's defined by attributes.

How to reproduce

Have one class defined by attributes, the others by annotations, and run orm:validate-schema

Expected behavior

Validation should use the same logic as generate to allow the use of both annotations and attributes.

Originally created by @oojacoboo on GitHub (Dec 15, 2021). ### Bug Report | Q | A |------------ | ------ | BC Break | no | Version | 2.11.x-dev #### Summary I should start by saying that we're in the process of updating our entities to use attributes instead of annotations. This is going to be a gradual process. Therefore, we need to support both, attributes and annotations, until that migration has completed. To do this, we've implemented a custom Mapping\Driver, as seen below: ```php <?php declare(strict_types = 1); namespace Acme\Adapter\Doctrine\ORM\Mapping; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\MappingException; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Psr\Log\LoggerInterface; /** * Custom Mapping\Driver * Implements both the AttributeDriver and AnnotationDriver. This allows for us * to use either attributes or annotations on our Doctrine entities. */ class Driver implements MappingDriver { public function __construct( protected AnnotationDriver $annotationDriver, protected AttributeDriver $attributeDriver, protected LoggerInterface $logger, protected bool $debug, ) { } /** @inheritdoc */ public function loadMetadataForClass($className, ClassMetadata $metadata): void { try { $this->attributeDriver->loadMetadataForClass($className, $metadata); $this->logEvent(sprintf('AttributeDriver utilized: %s', $className)); return; } catch (MappingException $me) { // Class X is not a valid entity, so try the other driver if (!preg_match('/^Class(.)*$/', $me->getMessage())) { // meh $this->logEvent('different MappingException: ' . $me->getMessage()); throw $me; } } $this->annotationDriver->loadMetadataForClass($className, $metadata); $this->logEvent(sprintf('AnnotationDriver utilized: %s', $className)); } /** @inheritdoc */ public function getAllClassNames(): array { return array_merge( $this->annotationDriver->getAllClassNames(), $this->attributeDriver->getAllClassNames(), ); } /** @inheritdoc */ public function isTransient($className): bool { return $this->attributeDriver->isTransient($className) || $this->annotationDriver->isTransient($className); } /** @inheritdoc */ private function logEvent(string $message): void { if (!$this->debug) { return; } $this->logger->info($message); } } ``` #### Current behavior This, when being used by the EntityManager (registered as the MetadataDriverImpl), works fine with the `orm:generate-proxies` command. However, when running the `orm:validate-schema`, it's unable to validate, failing to find the class that's defined by attributes. #### How to reproduce Have one class defined by attributes, the others by annotations, and run `orm:validate-schema` #### Expected behavior Validation should use the same logic as generate to allow the use of both annotations and attributes.
admin closed this issue 2026-01-22 15:40:45 +01:00
Author
Owner

@oojacoboo commented on GitHub (Dec 15, 2021):

It's also worth noting that this dual Driver approach is breaking the discriminator mapping entities.

Maybe some general advice around this approach would be valuable. I apologize if this has been discussed before somewhere. I wasn't able to find much available. The mapping class inspiration I found at https://github.com/helvete/hybrid_mapping_driver.

@oojacoboo commented on GitHub (Dec 15, 2021): It's also worth noting that this dual Driver approach is breaking the discriminator mapping entities. Maybe some general advice around this approach would be valuable. I apologize if this has been discussed before somewhere. I wasn't able to find much available. The mapping class inspiration I found at https://github.com/helvete/hybrid_mapping_driver.
Author
Owner

@beberlei commented on GitHub (Dec 18, 2021):

This is most likely due to the implementation of the driver and not a bug in Doctrine. I recommend that you look at the DriverChain and implement a new driver based off that with a whitelist of classes that are attribute-based instead of the namespace comparison. Please open the issue again if that didn't help.

@beberlei commented on GitHub (Dec 18, 2021): This is most likely due to the implementation of the driver and not a bug in Doctrine. I recommend that you look at the `DriverChain` and implement a new driver based off that with a whitelist of classes that are attribute-based instead of the namespace comparison. Please open the issue again if that didn't help.
Author
Owner

@oojacoboo commented on GitHub (Dec 19, 2021):

If anyone comes across this post looking for a solution, we've dropped the plan to support both annotation and attributes and instead used the doctrine rectors to do a full refactor.

https://github.com/rectorphp/rector

Using this configuration

$containerConfigurator->import(\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_ORM_29);
@oojacoboo commented on GitHub (Dec 19, 2021): If anyone comes across this post looking for a solution, we've dropped the plan to support both annotation and attributes and instead used the doctrine rectors to do a full refactor. https://github.com/rectorphp/rector Using this configuration ```php $containerConfigurator->import(\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_ORM_29); ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6889