ramsey / uuid-doctrine

:snowflake::file_cabinet: Allow the use of a ramsey/uuid UUID as Doctrine field type.
MIT License
892 stars 84 forks source link

Symfony/Doctrine fail to find an entity based off of its binary UUID #264

Open BenoitDuffez opened 3 months ago

BenoitDuffez commented 3 months ago

I have created a new Symfony app with version 7.0.7 and doctrine/orm ^3.1. I followed the instructions to use binary UUIDs and then created a CRUD for my entities. Entity A is standalone and works well. Entity B has a ManyToOne relation to A and while the form displays fine, I can't validate it and thus create a new B.

I was able to pin the issue here, in vendor/symfony/doctrine-bridge/Form/ChoiceList/ORMQueryBuilderLoader, line 70:

            // Filter out non-integer values (e.g. ""). If we don't, some
            // databases such as PostgreSQL fail.
            $values = array_values(array_filter($values, fn ($v) => (string) $v === (string) (int) $v || ctype_digit($v)));
        } elseif (\in_array($type, ['ulid', 'uuid', 'guid'])) { // <-----
            $parameterType = ArrayParameterType::STRING;

now if I modify this to:

            // Filter out non-integer values (e.g. ""). If we don't, some
            // databases such as PostgreSQL fail.
            $values = array_values(array_filter($values, fn ($v) => (string) $v === (string) (int) $v || ctype_digit($v)));
        } elseif (\in_array($type, ['ulid', 'uuid', 'guid', 'uuid_binary'])) {
            $parameterType = ArrayParameterType::STRING;

then it works like a charm.

I feel like I have missed something in the setup of this lib but I can't pinpoint what/where.

config/packages/doctrine.yaml

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'
        profiling_collect_backtrace: '%kernel.debug%'
        use_savepoints: true
        types:
            uuid_binary:  Ramsey\Uuid\Doctrine\UuidBinaryType

I also tried this in my Kernel but that does not work either:

public function boot(): void {
     parent::boot();
    \Doctrine\DBAL\Types\Type::addType('uuid_binary', 'Ramsey\Uuid\Doctrine\UuidBinaryType');
    $entityManager = $this->getContainer()->get('doctrine.orm.default_entity_manager');
    $entityManager->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('uuid_binary', 'binary');
}
subdee commented 3 months ago

.

BenoitDuffez commented 3 months ago

Your comment is valid for querying the database. Indeed we need to set a parameter to the bytes of the UUID. One note about your code though, I think it would be easier with

->setParameter(':column', $client->getId()->getBytes())

indeed, the getId returns a UUID that you can extract the bytes from.

However, my ticket was about forms. If you declare relationships between two entities, they are linked with their UUID and it works fine in the database. The problem is that when saving the form, the form data type is uuid_binary and Symfony has a static check that does not include this (but does include uuid).

Changing Symfony code is wrong, perhaps an upstream patch would be accepted but it also feels wrong to me. I don't know if there are events or configuration items that can be leveraged by this library so that Symfony knows how to handle uuid_binary.

PS: by "Symfony" I mean symfony/doctrine-bridge

subdee commented 3 months ago

@BenoitDuffez You are right, they are not related, I will make a separate issue for my problem.