Merge branch 'DDC-1643'
This commit is contained in:
commit
93f79d0810
@ -305,6 +305,7 @@ final class PersistentCollection implements Collection
|
|||||||
if ($this->association !== null &&
|
if ($this->association !== null &&
|
||||||
$this->association['isOwningSide'] &&
|
$this->association['isOwningSide'] &&
|
||||||
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
|
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
|
||||||
|
$this->owner &&
|
||||||
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
||||||
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
||||||
}
|
}
|
||||||
@ -759,4 +760,27 @@ final class PersistentCollection implements Collection
|
|||||||
|
|
||||||
return $this->coll->slice($offset, $length);
|
return $this->coll->slice($offset, $length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup internal state of cloned persistent collection.
|
||||||
|
*
|
||||||
|
* The following problems have to be prevented:
|
||||||
|
* 1. Added entities are added to old PC
|
||||||
|
* 2. New collection is not dirty, if reused on other entity nothing
|
||||||
|
* changes.
|
||||||
|
* 3. Snapshot leads to invalid diffs being generated.
|
||||||
|
* 4. Lazy loading grabs entities from old owner object.
|
||||||
|
* 5. New collection is connected to old owner and leads to duplicate keys.
|
||||||
|
*/
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
$this->initialize();
|
||||||
|
$this->owner = null;
|
||||||
|
|
||||||
|
if (is_object($this->coll)) {
|
||||||
|
$this->coll = clone $this->coll;
|
||||||
|
}
|
||||||
|
$this->snapshot = array();
|
||||||
|
$this->changed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -584,6 +584,23 @@ class UnitOfWork implements PropertyChangedListener
|
|||||||
|
|
||||||
$assoc = $class->associationMappings[$propName];
|
$assoc = $class->associationMappings[$propName];
|
||||||
|
|
||||||
|
// Persistent collection was exchanged with the "originally"
|
||||||
|
// created one. This can only mean it was cloned and replaced
|
||||||
|
// on another entity.
|
||||||
|
if ($actualValue instanceof PersistentCollection) {
|
||||||
|
$owner = $actualValue->getOwner();
|
||||||
|
if ($owner === null) { // cloned
|
||||||
|
$actualValue->setOwner($entity, $assoc);
|
||||||
|
} else if ($owner !== $entity) { // no clone, we have to fix
|
||||||
|
if (!$actualValue->isInitialized()) {
|
||||||
|
$actualValue->initialize(); // we have to do this otherwise the cols share state
|
||||||
|
}
|
||||||
|
$newValue = clone $actualValue;
|
||||||
|
$newValue->setOwner($entity, $assoc);
|
||||||
|
$class->reflFields[$propName]->setValue($entity, $newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($orgValue instanceof PersistentCollection) {
|
if ($orgValue instanceof PersistentCollection) {
|
||||||
// A PersistentCollection was de-referenced, so delete it.
|
// A PersistentCollection was de-referenced, so delete it.
|
||||||
$coid = spl_object_hash($orgValue);
|
$coid = spl_object_hash($orgValue);
|
||||||
|
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1643Test.php
Normal file
121
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1643Test.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||||
|
|
||||||
|
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||||
|
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group DDC-1643
|
||||||
|
*/
|
||||||
|
class DDC1643Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||||
|
{
|
||||||
|
private $user1;
|
||||||
|
private $user2;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->useModelSet('cms');
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$user1 = new CmsUser();
|
||||||
|
$user1->username = "beberlei";
|
||||||
|
$user1->name = "Benjamin";
|
||||||
|
$user1->status = "active";
|
||||||
|
$group1 = new CmsGroup();
|
||||||
|
$group1->name = "test";
|
||||||
|
$group2 = new CmsGroup();
|
||||||
|
$group2->name = "test";
|
||||||
|
$user1->addGroup($group1);
|
||||||
|
$user1->addGroup($group2);
|
||||||
|
$user2 = new CmsUser();
|
||||||
|
$user2->username = "romanb";
|
||||||
|
$user2->name = "Roman";
|
||||||
|
$user2->status = "active";
|
||||||
|
|
||||||
|
$this->_em->persist($user1);
|
||||||
|
$this->_em->persist($user2);
|
||||||
|
$this->_em->persist($group1);
|
||||||
|
$this->_em->persist($group2);
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$this->user1 = $this->_em->find(get_class($user1), $user1->id);
|
||||||
|
$this->user2 = $this->_em->find(get_class($user1), $user2->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClonePersistentCollectionAndReuse()
|
||||||
|
{
|
||||||
|
$user1 = $this->user1;
|
||||||
|
|
||||||
|
$user1->groups = clone $user1->groups;
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$user1 = $this->_em->find(get_class($user1), $user1->id);
|
||||||
|
|
||||||
|
$this->assertEquals(2, count($user1->groups));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClonePersistentCollectionAndShare()
|
||||||
|
{
|
||||||
|
$user1 = $this->user1;
|
||||||
|
$user2 = $this->user2;
|
||||||
|
|
||||||
|
$user2->groups = clone $user1->groups;
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$user1 = $this->_em->find(get_class($user1), $user1->id);
|
||||||
|
$user2 = $this->_em->find(get_class($user1), $user2->id);
|
||||||
|
|
||||||
|
$this->assertEquals(2, count($user1->groups));
|
||||||
|
$this->assertEquals(2, count($user2->groups));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloneThenDirtyPersistentCollection()
|
||||||
|
{
|
||||||
|
$user1 = $this->user1;
|
||||||
|
$user2 = $this->user2;
|
||||||
|
|
||||||
|
$group3 = new CmsGroup();
|
||||||
|
$group3->name = "test";
|
||||||
|
$user2->groups = clone $user1->groups;
|
||||||
|
$user2->groups->add($group3);
|
||||||
|
|
||||||
|
$this->_em->persist($group3);
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$user1 = $this->_em->find(get_class($user1), $user1->id);
|
||||||
|
$user2 = $this->_em->find(get_class($user1), $user2->id);
|
||||||
|
|
||||||
|
$this->assertEquals(3, count($user2->groups));
|
||||||
|
$this->assertEquals(2, count($user1->groups));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNotCloneAndPassAroundFlush()
|
||||||
|
{
|
||||||
|
$user1 = $this->user1;
|
||||||
|
$user2 = $this->user2;
|
||||||
|
|
||||||
|
$group3 = new CmsGroup();
|
||||||
|
$group3->name = "test";
|
||||||
|
$user2->groups = $user1->groups;
|
||||||
|
$user2->groups->add($group3);
|
||||||
|
|
||||||
|
$this->assertEQuals(1, count($user1->groups->getInsertDiff()));
|
||||||
|
|
||||||
|
$this->_em->persist($group3);
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
|
||||||
|
$user1 = $this->_em->find(get_class($user1), $user1->id);
|
||||||
|
$user2 = $this->_em->find(get_class($user1), $user2->id);
|
||||||
|
|
||||||
|
$this->assertEquals(3, count($user2->groups));
|
||||||
|
$this->assertEquals(3, count($user1->groups));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user