Skip to content

Problem while hydrating a Doctrine Entity when using an interface as alias. #2832

Open
@pink6440

Description

@pink6440
  • Using symfony 7.3

With Doctrine ORM, when aliasing a concrete class to an interface, the hydratation process does not work anymore.

Doctrine configuration

doctrine:
  orm:
    resolve_target_entities:
      MyInterface: App\Entity\MyRealEntity

In the component

    #[LiveProp()]
    public ?MyInterface $initialFormData = null;

The error

An exception has been thrown during the rendering of a template ("Cannot dehydrate value typed as interface

The workaround

I have a workaround but i'mnot really used about what to do to help, so, guidelines are welcome :)

Digging into DoctrineEntityHydrationExtension.php :

  private function objectManagerFor(string $class): ?ObjectManager
    {
        if (!interface_exists($class) && !class_exists($class)) {
            return null;
        }

        // todo cache/warmup an array of classes that are "doctrine objects"
        foreach ($this->managerRegistries as $registry) {
            foreach($registry->getManagers() as $om) {
                // this way, it resolve the interface
                if ($om->getClassMetadata($class)) {
                    return self::ensureManagedObject($om, $class);
                }
            }
            // does not work with interface
            // if ($om = $registry->getManagerForClass($class)) {
            //    return self::ensureManagedObject($om, $class);
            // }
        }

        return null;
    }

Activity

changed the title [-]Problem white Hydrating a Doctrine Entity when using an interface as alias.[/-] [+]Problem while hydrating a Doctrine Entity when using an interface as alias.[/+] on Jun 11, 2025
Kocal

Kocal commented on Jun 11, 2025

@Kocal
Member

I'm far from being a Doctrine expert, and at this moment I don't know how we can use this resolve_target_entities setting / Doctrine API to get your MyInterface being correctly hydrated.

As a short solution, I can suggest you to create your own Hydration Extension:

  • supports MyInterface
  • hydrate from an id to your MyInterface entity
  • dehydrate from your MyInterface entity to an id
pink6440

pink6440 commented on Jun 12, 2025

@pink6440
Author

Hi Kocal,

I'm agree about create a specific hydrator.

However, it is still possible to make the current doctrine hydrator to work if objectManagerFor method is updated.

First problem to solve : find the object manager wich is responsible for the final entity

$interface = UserInterface::class;

// current method : that won't work
$em = $this->doctrine->getManagerForClass($interface);
$io->writeln("Manager (method 1) : ".($em ? get_class($em) : "not found"));

// second method : that works

foreach($this->doctrine->getManagers() as $em){
    if ($em->getClassMetadata($interface)) {
        // founded !
        $io->writeln("Manager (method 2) : ".get_class($em));
    }

}

Second problem : how to hydrate/dehydrate the entity from the interface
According to the existing code of the doctrine hydrator, it already works.

 public function dehydrate(object $object): mixed
    {
       // if the object manager is resolved, it will provide the right metadata for the final class 
        $id = $this
            ->objectManagerFor($class = $object::class)
            ->getClassMetadata($class)
            ->getIdentifierValues($object)
        ;

And same goes for hydrate process (the find method resolves nicely if using interface)

 public function hydrate(mixed $value, string $className): ?object
    {
        // an empty array means a non-persisted entity
        // we support instantiating with no constructor args
        if (\is_array($value) && 0 === \count($value)) {
            return new $className();
        }

        // e.g. an empty string
        if (!$value) {
            return null;
        }

        // $data is a single identifier or array of identifiers
        if (\is_scalar($value) || \is_array($value)) {
            return $this->objectManagerFor($className)->find($className, $value);
        }

        throw new \InvalidArgumentException(\sprintf('Cannot hydrate Doctrine entity "%s". Value of type "%s" is not supported.', $className, get_debug_type($value)));
    }
Kocal

Kocal commented on Jun 12, 2025

@Kocal
Member

Hi, and thanks for your investigation :)

Do you want to open a PR?

pink6440

pink6440 commented on Jun 12, 2025

@pink6440
Author

Thanks you too for the already done work !
And I will try to make the pm.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @Kocal@pink6440@carsonbot

      Issue actions

        Problem while hydrating a Doctrine Entity when using an interface as alias. · Issue #2832 · symfony/ux