From e44f413b00813a9dc7fa793695589d6a648db144 Mon Sep 17 00:00:00 2001 From: Elias Luhr Date: Wed, 1 Jul 2026 17:21:39 +0200 Subject: [PATCH] fix(uuid): PostgreSQL-compatible partial-UUID resolution [*] UuidResolver::findByPartialBinaryUuid built raw MySQL-only SQL (`SELECT BIN_TO_UUID(id) ... WHERE LOWER(HEX(id)) LIKE ...`). On PostgreSQL, where the Symfony `uuid` type is a native `uuid` column (not BINARY(16)), this threw `function bin_to_uuid(uuid) does not exist` and every partial-UUID lookup returned HTTP 500. Extract the lookup into a public static buildPartialUuidSql() that branches on the platform: `id::text` + REPLACE on PostgreSQL, HEX() + BIN_TO_UUID() on MySQL/MariaDB. Both yield the canonical UUID string and match the same hyphen-stripped hex prefix. Also resolve the id column via metadata instead of hardcoding "id". Adds UuidResolverTest asserting the generated SQL per platform. Co-Authored-By: Claude Opus 4.8 --- src/Service/UuidResolver.php | 40 +++++++++++++++++++++++++++--- tests/Service/UuidResolverTest.php | 38 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 tests/Service/UuidResolverTest.php diff --git a/src/Service/UuidResolver.php b/src/Service/UuidResolver.php index 423d94b..e03bee3 100644 --- a/src/Service/UuidResolver.php +++ b/src/Service/UuidResolver.php @@ -5,6 +5,8 @@ namespace Dmstr\ApiPlatformUtils\Service; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Uid\Uuid; @@ -59,17 +61,20 @@ public function findByPartialUuid(string $entityClass, string $partialId): ?obje } /** - * Find entity with binary UUID storage using native SQL + * Find entity with UUID storage using native SQL. * - * Uses HEX(id) to convert binary UUID to string for LIKE matching + * The Symfony `uuid` type stores the value differently per platform — a + * BINARY(16) on MySQL/MariaDB, a native `uuid` column on PostgreSQL — so the + * partial-prefix match must be built per platform (see {@see buildPartialUuidSql}). */ private function findByPartialBinaryUuid(string $entityClass, string $partialId): ?object { $metadata = $this->entityManager->getClassMetadata($entityClass); $tableName = $metadata->getTableName(); + $idColumn = $metadata->getColumnName('id'); $conn = $this->entityManager->getConnection(); - $sql = "SELECT BIN_TO_UUID(id) as uuid_str FROM {$tableName} WHERE LOWER(HEX(id)) LIKE :partialId LIMIT 2"; + $sql = self::buildPartialUuidSql($conn->getDatabasePlatform(), $tableName, $idColumn); $stmt = $conn->prepare($sql); $stmt->bindValue('partialId', strtolower(str_replace('-', '', $partialId)) . '%'); $resultSet = $stmt->executeQuery(); @@ -89,6 +94,35 @@ private function findByPartialBinaryUuid(string $entityClass, string $partialId) return $this->entityManager->getRepository($entityClass)->find($fullUuid); } + /** + * Build the native partial-UUID lookup SQL for the given platform. + * + * Both branches select the canonical hyphenated UUID string as `uuid_str` + * and match the caller's hyphen-stripped, lower-cased hex prefix: + * - PostgreSQL: the column is a native `uuid`; cast to text and strip the + * hyphens (`REPLACE(LOWER(id::text), '-', '')`). + * - MySQL/MariaDB: the column is BINARY(16); `HEX()` yields the 32-char hex + * and `BIN_TO_UUID()` reads it back as a canonical string. + * + * Public + static so it can be unit-tested per platform without a database. + */ + public static function buildPartialUuidSql(AbstractPlatform $platform, string $tableName, string $idColumn): string + { + if ($platform instanceof PostgreSQLPlatform) { + return sprintf( + "SELECT %2\$s::text AS uuid_str FROM %1\$s WHERE REPLACE(LOWER(%2\$s::text), '-', '') LIKE :partialId LIMIT 2", + $tableName, + $idColumn + ); + } + + return sprintf( + 'SELECT BIN_TO_UUID(%2$s) AS uuid_str FROM %1$s WHERE LOWER(HEX(%2$s)) LIKE :partialId LIMIT 2', + $tableName, + $idColumn + ); + } + /** * Find entity with string UUID storage using DQL * diff --git a/tests/Service/UuidResolverTest.php b/tests/Service/UuidResolverTest.php new file mode 100644 index 0000000..5895ebe --- /dev/null +++ b/tests/Service/UuidResolverTest.php @@ -0,0 +1,38 @@ +