BackedEnum primary key fails to convert for association using proxy classes #7172

Closed
opened 2026-01-22 15:46:02 +01:00 by admin · 9 comments
Owner

Originally created by @wmouwen on GitHub (Jun 22, 2023).

Bug Report

Q A
BC Break unsure
Version 2.15.3

Summary

For a while BackedEnums were allowed as primary keys, where Doctrine would happily convert them to their scalar value. There has been some back and forth about this in previous issues/PRs where the functionality was repaired and broken again.

https://github.com/doctrine/orm/issues/10334
https://github.com/doctrine/orm/issues/10471
https://github.com/doctrine/orm/pull/10508

Most recent events:
https://github.com/doctrine/orm/issues/10745
https://github.com/doctrine/orm/pull/10758

Current behavior

Setting a BackedEnum as primary key on an entity, using that entity in an association and trying to save it with a proxy class, will throw an error as the BackedEnum is no longer converted to a scalar value.

Uncaught Error: Object of class Enum\LocaleCode could not be converted to string in /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:43
Stack trace:
#0 /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php(43): PDOStatement->bindValue(1, Object(Enum\LocaleCode), 2)
#1 /app/vendor/doctrine/dbal/src/Statement.php(115): Doctrine\DBAL\Driver\PDO\Statement->bindValue(1, Object(Enum\LocaleCode), 2)
#2 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(276): Doctrine\DBAL\Statement->bindValue(1, Object(Enum\LocaleCode), Object(Doctrine\DBAL\Types\StringType))
#3 /app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1172): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts()
#4 /app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(441): Doctrine\ORM\UnitOfWork->executeInserts(Object(Doctrine\ORM\Mapping\ClassMetadata))
#5 /app/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(403): Doctrine\ORM\UnitOfWork->commit(NULL)
#6 /app/trigger_error.php(23): Doctrine\ORM\EntityManager->flush()
#7 {main}
  thrown in /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php on line 43

How to reproduce

See https://github.com/wmouwen/doctrine-orm-10788 for a minimal setup throwing the error.

Code snippet copy 👇
<?php

enum LocaleCode: string
{
    case Dutch = 'nl_NL';
}

#[ORM\Entity, ORM\Table]
class Locale
{
    #[ORM\Id]
    #[ORM\Column(name: 'code', type: Types::STRING, enumType: LocaleCode::class)]
    public LocaleCode $code;
}

#[ORM\Entity, ORM\Table]
class CategoryLocale
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(name: 'id', type: Types::INTEGER)]
    public int $id;

    #[ORM\ManyToOne(targetEntity: \Entity\Locale::class, cascade: ['persist'])]
    #[ORM\JoinColumn(name: 'locale_code', referencedColumnName: 'code', nullable: false)]
    public Locale $locale;
}

global $entityManager;

// Create entity with BackedEnum as primary key.
$locale = new \Entity\Locale();
$locale->code = \Enum\LocaleCode::Dutch;

// Persist entity, clear manager.
$entityManager->persist($locale);
$entityManager->flush();
$entityManager->clear();

// Create entity with an association to the entity with BackedEnum key.
// Note that the use of a proxy class is required for the error to show.
$categoryLocale = new \Entity\CategoryLocale();
$categoryLocale->locale = $entityManager->getReference(\Entity\Locale::class, \Enum\LocaleCode::Dutch);

// Attempt to persist, throws an error.
$entityManager->persist($categoryLocale);
$entityManager->flush();

Expected behavior

The BackedEnum is converted and the save succeeds, as it did in version 2.15.2.

-edit- Added the stack trace, made the code snippet collapse, added reference to repository with minimal setup.

Originally created by @wmouwen on GitHub (Jun 22, 2023). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | unsure | Version | 2.15.3 #### Summary For a while BackedEnums were allowed as primary keys, where Doctrine would happily convert them to their scalar value. There has been some back and forth about this in previous issues/PRs where the functionality was repaired and broken again. https://github.com/doctrine/orm/issues/10334 https://github.com/doctrine/orm/issues/10471 https://github.com/doctrine/orm/pull/10508 Most recent events: https://github.com/doctrine/orm/issues/10745 https://github.com/doctrine/orm/pull/10758 #### Current behavior Setting a BackedEnum as primary key on an entity, using that entity in an association and trying to save it with a proxy class, will throw an error as the BackedEnum is no longer converted to a scalar value. ``` Uncaught Error: Object of class Enum\LocaleCode could not be converted to string in /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:43 Stack trace: #0 /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php(43): PDOStatement->bindValue(1, Object(Enum\LocaleCode), 2) #1 /app/vendor/doctrine/dbal/src/Statement.php(115): Doctrine\DBAL\Driver\PDO\Statement->bindValue(1, Object(Enum\LocaleCode), 2) #2 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(276): Doctrine\DBAL\Statement->bindValue(1, Object(Enum\LocaleCode), Object(Doctrine\DBAL\Types\StringType)) #3 /app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1172): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts() #4 /app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(441): Doctrine\ORM\UnitOfWork->executeInserts(Object(Doctrine\ORM\Mapping\ClassMetadata)) #5 /app/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(403): Doctrine\ORM\UnitOfWork->commit(NULL) #6 /app/trigger_error.php(23): Doctrine\ORM\EntityManager->flush() #7 {main} thrown in /app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php on line 43 ``` #### How to reproduce See https://github.com/wmouwen/doctrine-orm-10788 for a minimal setup throwing the error. <details> <summary>Code snippet copy 👇</summary> ```php <?php enum LocaleCode: string { case Dutch = 'nl_NL'; } #[ORM\Entity, ORM\Table] class Locale { #[ORM\Id] #[ORM\Column(name: 'code', type: Types::STRING, enumType: LocaleCode::class)] public LocaleCode $code; } #[ORM\Entity, ORM\Table] class CategoryLocale { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(name: 'id', type: Types::INTEGER)] public int $id; #[ORM\ManyToOne(targetEntity: \Entity\Locale::class, cascade: ['persist'])] #[ORM\JoinColumn(name: 'locale_code', referencedColumnName: 'code', nullable: false)] public Locale $locale; } ``` ```php global $entityManager; // Create entity with BackedEnum as primary key. $locale = new \Entity\Locale(); $locale->code = \Enum\LocaleCode::Dutch; // Persist entity, clear manager. $entityManager->persist($locale); $entityManager->flush(); $entityManager->clear(); // Create entity with an association to the entity with BackedEnum key. // Note that the use of a proxy class is required for the error to show. $categoryLocale = new \Entity\CategoryLocale(); $categoryLocale->locale = $entityManager->getReference(\Entity\Locale::class, \Enum\LocaleCode::Dutch); // Attempt to persist, throws an error. $entityManager->persist($categoryLocale); $entityManager->flush(); ``` </details> #### Expected behavior The BackedEnum is converted and the save succeeds, as it did in version `2.15.2`. _-edit- Added the stack trace, made the code snippet collapse, added reference to repository with minimal setup._
admin closed this issue 2026-01-22 15:46:02 +01:00
Author
Owner

@wmouwen commented on GitHub (Jun 22, 2023):

@Gwemox Allow me to tag you in this issue as you were the author of the initial pull requests.

@wmouwen commented on GitHub (Jun 22, 2023): @Gwemox Allow me to tag you in this issue as you were the author of the initial pull requests.
Author
Owner

@Gwemox commented on GitHub (Jun 22, 2023):

@wmouwen Do you have the stack trace?

@Gwemox commented on GitHub (Jun 22, 2023): @wmouwen Do you have the stack trace?
Author
Owner

@wmouwen commented on GitHub (Jun 22, 2023):

@Gwemox I've created a repository with minimal code which triggers the error. Important to the case is the use of a proxy class. Stack trace is included in the README in the repository.

https://github.com/wmouwen/doctrine-orm-10788

@wmouwen commented on GitHub (Jun 22, 2023): @Gwemox I've created a repository with minimal code which triggers the error. Important to the case is the use of a proxy class. Stack trace is included in the README in the repository. https://github.com/wmouwen/doctrine-orm-10788
Author
Owner

@Gwemox commented on GitHub (Jun 23, 2023):

@wmouwen you can convert your backed enum to string with a doctrine custom type : https://www.doctrine-project.org/projects/doctrine-orm/en/2.15/cookbook/custom-mapping-types.html

@greg0ire @sips-richard We should think about the desired solution for 2.6 or 3.0 ?

Currently StringType does nothing.

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

$value is a backed enum.

@Gwemox commented on GitHub (Jun 23, 2023): @wmouwen you can convert your backed enum to string with a doctrine custom type : https://www.doctrine-project.org/projects/doctrine-orm/en/2.15/cookbook/custom-mapping-types.html @greg0ire @sips-richard We should think about the desired solution for 2.6 or 3.0 ? Currently StringType does nothing. ```php public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value; } ``` $value is a backed enum.
Author
Owner

@greg0ire commented on GitHub (Jun 23, 2023):

Well if it's a bug, for 2.15

@greg0ire commented on GitHub (Jun 23, 2023): Well if it's a bug, for 2.15
Author
Owner

@Gwemox commented on GitHub (Jun 23, 2023):

We reverted because of this issue https://github.com/doctrine/orm/issues/10745

We could modify the StringType to make a ->value if it is an enum ? Or create EnumType ?

@Gwemox commented on GitHub (Jun 23, 2023): We reverted because of this issue https://github.com/doctrine/orm/issues/10745 We could modify the StringType to make a `->value` if it is an enum ? Or create EnumType ?
Author
Owner

@wmouwen commented on GitHub (Jun 23, 2023):

After having fiddled with custom types for backed enumerations, I would argue having dedicated types isn't the way to go. You would have to differ between the (kinds of) string and integers that there are default mappings for: EnumStringType, EnumIntegerType, optionally EnumSmallintType, etc.

@wmouwen commented on GitHub (Jun 23, 2023): After having fiddled with custom types for backed enumerations, I would argue having dedicated types isn't the way to go. You would have to differ between the (kinds of) string and integers that there are default mappings for: EnumStringType, EnumIntegerType, optionally EnumSmallintType, etc.
Author
Owner

@wmouwen commented on GitHub (Jul 7, 2025):

The bug had been gone for a while without it actually having received a fix. Now, with the return of lazy objects in v3.5, the bug has returned.

If you set enable_native_lazy_objects = true and enable_lazy_ghost_objects = false, the conversion from BackedEnum to its backing value no longer occurs.

Error

{
  "@timestamp": "2025-07-07T10:14:47.991714+02:00",
  "@version": 1,
  "host": "a147af0648d6",
  "message": "Uncaught Error: Object of class App\\Enum\\CountryAlpha2 could not be converted to string",
  "type": "app",
  "channel": "php",
  "level": "CRITICAL",
  "monolog_level": 500,
  "context": {
    "exception": {
      "class": "Error",
      "message": "Object of class App\\Enum\\CountryAlpha2 could not be converted to string",
      "code": 0,
      "file": "/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:26",
      "trace": [
        "/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:26",
        "/app/vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:19",
        "/app/vendor/doctrine/dbal/src/Logging/Statement.php:35",
        "/app/vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:19",
        "/app/vendor/symfony/doctrine-bridge/Middleware/Debug/Statement.php:46",
        "/app/vendor/doctrine/dbal/src/Statement.php:93",
        "/app/vendor/doctrine/orm/src/Persisters/Entity/BasicEntityPersister.php:257",
        "/app/vendor/doctrine/orm/src/UnitOfWork.php:1057",
        "/app/vendor/doctrine/orm/src/UnitOfWork.php:402",
        "/app/vendor/doctrine/orm/src/EntityManager.php:268",
        "/app/database/fixtures/WebshopFixtures.php:60",
        "/app/database/fixtures/AbstractDoctrineFixture.php:52",
        "/app/vendor/doctrine/data-fixtures/src/Executor/AbstractExecutor.php:86",
        "/app/vendor/doctrine/data-fixtures/src/Executor/ORMExecutor.php:26",
        "/app/vendor/doctrine/orm/src/EntityManager.php:187",
        "/app/vendor/doctrine/data-fixtures/src/Executor/ORMExecutor.php:20",
        "/app/vendor/doctrine/doctrine-fixtures-bundle/src/Command/LoadDataFixturesDoctrineCommand.php:139",
        "/app/vendor/symfony/console/Command/Command.php:318",
        "/app/vendor/symfony/console/Application.php:1092",
        "/app/vendor/symfony/framework-bundle/Console/Application.php:123",
        "/app/vendor/symfony/console/Application.php:341",
        "/app/vendor/symfony/framework-bundle/Console/Application.php:77",
        "/app/vendor/symfony/console/Application.php:192",
        "/app/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:49",
        "/app/vendor/autoload_runtime.php:29",
        "/app/bin/console:15"
      ]
    }
  }
}

Package versions

# composer show -i "doctrine/*"
doctrine/annotations                2.0.2  Docblock Annotations Parser
doctrine/collections                2.3.0  PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.
doctrine/common                     3.5.0  PHP Doctrine Common project is a library that provides additional functionality that oth...
doctrine/data-fixtures              2.0.3  Data Fixtures for all Doctrine Object Managers
doctrine/dbal                       4.2.4  Powerful PHP database abstraction layer (DBAL) with many features for database schema in...
doctrine/deprecations               1.1.5  A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options t...
doctrine/doctrine-bundle            2.15.0 Symfony DoctrineBundle
doctrine/doctrine-fixtures-bundle   4.1.0  Symfony DoctrineFixturesBundle
doctrine/doctrine-migrations-bundle 3.4.2  Symfony DoctrineMigrationsBundle
doctrine/event-manager              2.0.1  The Doctrine Event Manager is a simple PHP event system that was built to be used with t...
doctrine/inflector                  2.0.10 PHP Doctrine Inflector is a small library that can perform string manipulations with reg...
doctrine/instantiator               2.0.0  A small, lightweight utility to instantiate objects in PHP without invoking their constr...
doctrine/lexer                      3.0.1  PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.
doctrine/migrations                 3.9.1  PHP Doctrine Migrations project offer additional functionality on top of the database ab...
doctrine/orm                        3.5.0  Object-Relational-Mapper for PHP
doctrine/persistence                4.0.0  The Doctrine Persistence project is a set of shared interfaces and functionality that th...
doctrine/sql-formatter              1.5.2  a PHP SQL highlighting library

@wmouwen commented on GitHub (Jul 7, 2025): The bug had been gone for a while without it actually having received a fix. Now, with the return of lazy objects in v3.5, the bug has returned. If you set `enable_native_lazy_objects = true` and `enable_lazy_ghost_objects = false`, the conversion from BackedEnum to its backing value no longer occurs. <details><summary>Error</summary> <p> ```json { "@timestamp": "2025-07-07T10:14:47.991714+02:00", "@version": 1, "host": "a147af0648d6", "message": "Uncaught Error: Object of class App\\Enum\\CountryAlpha2 could not be converted to string", "type": "app", "channel": "php", "level": "CRITICAL", "monolog_level": 500, "context": { "exception": { "class": "Error", "message": "Object of class App\\Enum\\CountryAlpha2 could not be converted to string", "code": 0, "file": "/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:26", "trace": [ "/app/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:26", "/app/vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:19", "/app/vendor/doctrine/dbal/src/Logging/Statement.php:35", "/app/vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:19", "/app/vendor/symfony/doctrine-bridge/Middleware/Debug/Statement.php:46", "/app/vendor/doctrine/dbal/src/Statement.php:93", "/app/vendor/doctrine/orm/src/Persisters/Entity/BasicEntityPersister.php:257", "/app/vendor/doctrine/orm/src/UnitOfWork.php:1057", "/app/vendor/doctrine/orm/src/UnitOfWork.php:402", "/app/vendor/doctrine/orm/src/EntityManager.php:268", "/app/database/fixtures/WebshopFixtures.php:60", "/app/database/fixtures/AbstractDoctrineFixture.php:52", "/app/vendor/doctrine/data-fixtures/src/Executor/AbstractExecutor.php:86", "/app/vendor/doctrine/data-fixtures/src/Executor/ORMExecutor.php:26", "/app/vendor/doctrine/orm/src/EntityManager.php:187", "/app/vendor/doctrine/data-fixtures/src/Executor/ORMExecutor.php:20", "/app/vendor/doctrine/doctrine-fixtures-bundle/src/Command/LoadDataFixturesDoctrineCommand.php:139", "/app/vendor/symfony/console/Command/Command.php:318", "/app/vendor/symfony/console/Application.php:1092", "/app/vendor/symfony/framework-bundle/Console/Application.php:123", "/app/vendor/symfony/console/Application.php:341", "/app/vendor/symfony/framework-bundle/Console/Application.php:77", "/app/vendor/symfony/console/Application.php:192", "/app/vendor/symfony/runtime/Runner/Symfony/ConsoleApplicationRunner.php:49", "/app/vendor/autoload_runtime.php:29", "/app/bin/console:15" ] } } } ``` </p> </details> <details><summary>Package versions</summary> <p> ``` # composer show -i "doctrine/*" doctrine/annotations 2.0.2 Docblock Annotations Parser doctrine/collections 2.3.0 PHP Doctrine Collections library that adds additional functionality on top of PHP arrays. doctrine/common 3.5.0 PHP Doctrine Common project is a library that provides additional functionality that oth... doctrine/data-fixtures 2.0.3 Data Fixtures for all Doctrine Object Managers doctrine/dbal 4.2.4 Powerful PHP database abstraction layer (DBAL) with many features for database schema in... doctrine/deprecations 1.1.5 A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options t... doctrine/doctrine-bundle 2.15.0 Symfony DoctrineBundle doctrine/doctrine-fixtures-bundle 4.1.0 Symfony DoctrineFixturesBundle doctrine/doctrine-migrations-bundle 3.4.2 Symfony DoctrineMigrationsBundle doctrine/event-manager 2.0.1 The Doctrine Event Manager is a simple PHP event system that was built to be used with t... doctrine/inflector 2.0.10 PHP Doctrine Inflector is a small library that can perform string manipulations with reg... doctrine/instantiator 2.0.0 A small, lightweight utility to instantiate objects in PHP without invoking their constr... doctrine/lexer 3.0.1 PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers. doctrine/migrations 3.9.1 PHP Doctrine Migrations project offer additional functionality on top of the database ab... doctrine/orm 3.5.0 Object-Relational-Mapper for PHP doctrine/persistence 4.0.0 The Doctrine Persistence project is a set of shared interfaces and functionality that th... doctrine/sql-formatter 1.5.2 a PHP SQL highlighting library ``` </p> </details>
Author
Owner

@wmouwen commented on GitHub (Jul 14, 2025):

#12063

@wmouwen commented on GitHub (Jul 14, 2025): #12063
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7172