Getting a “true” object from a proxy object in doctrine2

前端 未结 11 1604
南笙
南笙 2021-02-01 13:50

Doctrine uses proxy objects to represent related objects in order to facilitate lazy loading. This is a really cool feature, but its causing an issue with something I am trying

11条回答
  •  甜味超标
    2021-02-01 14:49

    You get an unique instance of an entity from Doctrine. If you request it two times, you'll always get the same object.

    As a consequence, if your entity is lazy-loaded first (via a @ManyToOne somewhere for example), this entity instance will be a Proxy.

    Example:

    You have a User entity having a bidirectional @OneToOne on a Config entity...

    Case 1

    You request your User:

    • you get a real instance of User
    • $user->config will contain a Proxy

    If later on you request the same Config entity in any piece of your app, you'll end up with that proxy.

    Case 2

    You request your Config and your User has never been imported before:

    • you get a real instance of Config
    • $config->user will contain a Proxy

    If later on you request the same User entity in any piece of your app, you'll end up with that proxy.


    All in all, querying again for the same entity will still end up to a proxy (which is an instance of your User anyway, because the generated proxy extends from it).

    If you really need a second instance of your entity that is a real one (if some of your app logic does a get_class that you can't replace by instanceof for example), you can try to play with $em->detach() but it will be a nightmare (and thus your app may behave with even more magic than Doctrine already bring).

    A solution (coming from my dark side, I assume) can be recreating a non-managed Entity manually.

    public function getRealEntity($proxy)
    {
        if ($proxy instanceof Doctrine\ORM\Proxy\Proxy) {
            $metadata              = $this->getManager()->getMetadataFactory()->getMetadataFor(get_class($proxy));
            $class                 = $metadata->getName();
            $entity                = new $class();
            $reflectionSourceClass = new \ReflectionClass($proxy);
            $reflectionTargetClass = new \ReflectionClass($entity);
            foreach ($metadata->getFieldNames() as $fieldName) {
                $reflectionPropertySource = $reflectionSourceClass->getProperty($fieldName);
                $reflectionPropertySource->setAccessible(true);
                $reflectionPropertyTarget = $reflectionTargetClass->getProperty($fieldName);
                $reflectionPropertyTarget->setAccessible(true);
                $reflectionPropertyTarget->setValue($entity, $reflectionPropertySource->getValue($proxy));
            }
    
            return $entity;
        }
    
        return $proxy;
    }
    

提交回复
热议问题