[DDC-2409] Merge operation tries to add new detached entities to indentity map and load them as proxies Created: 22/Apr/13  Updated: 05/May/13  Resolved: 01/May/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.3.1
Fix Version/s: 2.4, 2.3.4
Security Level: All

Type: Bug Priority: Major
Reporter: Oleg Namaka Assignee: Fabio B. Silva
Resolution: Fixed Votes: 0
Labels: None


 Description   
class A
    {
       /**
        *  @ORM\ManyToOne(targetEntity= "B"...
        *  @ORM\JoinColumn(name=" ...
        */
        protected $b;
        
        public function getB()
        {
            return $this->b;
        }
        
        public function setB($b)
        {
            $this->b = $b;
        }
}
    
    class B
	{
       /**
        * As
        *
        * @var \Doctrine\Common\Collections\Collection
        *
        * @ORM\OneToMany(targetEntity="A", mappedBy="B")
        */
		protected $As;
    }
	
	$b = new \B();
	
	$a = $em->find('A', 123);
	
	$a->setB($b);
	
	$em->detach($a);
	
	$em->detach($b);
	
	$b = $em->merge($b); // notice that $b now is merged
	
	$a = $em->merge($a); //  hangs as it creates the proxy for $b and tries to load a though __load even though $b is already managed

Couple of possible issues in the following code from doMerge:

if ($assoc2['type'] & ClassMetadata::TO_ONE) {
                        $other = $prop->getValue($entity);
                        if ($other === null) {
                            $prop->setValue($managedCopy, null);
                        } else if ($other instanceof Proxy && !$other->__isInitialized__) {
                            // do not merge fields marked lazy that have not been fetched.
                            continue;
                        } else if ( ! $assoc2['isCascadeMerge']) {
                            if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) {
                                $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
                                $relatedId = $targetClass->getIdentifierValues($other);

                                if ($targetClass->subClasses) {
                                    $other = $this->em->find($targetClass->name, $relatedId);
                                } else {
                                    $other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
                                    $this->registerManaged($other, $relatedId, array());
                                }
                            }
                            $prop->setValue($managedCopy, $other);
                        }
     $relatedId = $targetClass->getIdentifierValues($other);

$relatedId is emply as the detached $other was never flushed. It should never be used to add this entity to the identityMap ($this->registerManaged($other, $relatedId, array())

$other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);

This should never use the proxy factory for new detached entities as they are already merged back ($b). This method seems to not have any means to find managed $b.

If $b = $em->merge($b); was not called, the method would probably have worked but I think it is not right to rely on that calling or not calling certain methods or their order.



 Comments   
Comment by Benjamin Eberlei [ 01/May/13 ]

Merged Fabios Pull Request

Comment by Oleg Namaka [ 01/May/13 ]

https://github.com/doctrine/doctrine2/pull/655

Generated at Wed Apr 23 16:11:49 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.