DDC-501 - Cascade Merging unitialized Persistent Collections leads to weird behaviour
This commit is contained in:
parent
13affb2eb2
commit
f7e8109d07
@ -199,7 +199,7 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
private function _initialize()
|
||||
{
|
||||
if ( ! $this->_initialized) {
|
||||
if ( ! $this->_initialized && $this->_association) {
|
||||
if ($this->_isDirty) {
|
||||
// Has NEW objects added through add(). Remember them.
|
||||
$newObjects = $this->_coll->toArray();
|
||||
@ -580,7 +580,7 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return array('_coll');
|
||||
return array('_coll', '_initialized');
|
||||
}
|
||||
|
||||
/* ArrayAccess implementation */
|
||||
|
@ -1366,6 +1366,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ( ! isset($class->associationMappings[$name])) {
|
||||
$prop->setValue($managedCopy, $prop->getValue($entity));
|
||||
} else {
|
||||
// why $assoc2? See the method signature, there is $assoc already!
|
||||
$assoc2 = $class->associationMappings[$name];
|
||||
if ($assoc2->isOneToOne()) {
|
||||
if ( ! $assoc2->isCascadeMerge) {
|
||||
@ -1379,6 +1380,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$mergeCol = $prop->getValue($entity);
|
||||
if ($mergeCol instanceof PersistentCollection && !$mergeCol->isInitialized()) {
|
||||
// keep the lazy persistent collection of the managed copy.
|
||||
continue;
|
||||
}
|
||||
|
||||
$coll = new PersistentCollection($this->_em,
|
||||
$this->_em->getClassMetadata($assoc2->targetEntityName),
|
||||
new ArrayCollection
|
||||
|
@ -40,7 +40,7 @@ class CmsUser
|
||||
*/
|
||||
public $address;
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist"})
|
||||
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"})
|
||||
* @JoinTable(name="cms_users_groups",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
|
||||
|
128
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php
Normal file
128
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
use Doctrine\Tests\Models\CMS\CmsAddress;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* ----------------- !! NOTE !! --------------------
|
||||
* To reproduce the manyToMany-Bug it's necessary
|
||||
* to cascade "merge" on cmUser::groups
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* @PHP-Version 5.3.2
|
||||
* @PHPUnit-Version 3.4.11
|
||||
*
|
||||
* @author markus
|
||||
*/
|
||||
class DDC501Test extends OrmFunctionalTestCase {
|
||||
protected function setUp() {
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testCreateUser()
|
||||
{
|
||||
### Create User
|
||||
$user = $this->createAndPersistUser();
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertTrue($this->_em->contains($user));
|
||||
$this->_em->clear();
|
||||
$this->assertFalse($this->_em->contains($user));
|
||||
|
||||
unset($user);
|
||||
|
||||
### Reload User from DB *without* any associations
|
||||
$userReloaded = $this->loadUserFromEntityManager();
|
||||
|
||||
$this->assertTrue($this->_em->contains($userReloaded));
|
||||
$this->_em->clear();
|
||||
$this->assertFalse($this->_em->contains($userReloaded));
|
||||
|
||||
// freeze and unfreeze
|
||||
$userClone = unserialize(serialize($userReloaded));
|
||||
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $userClone);
|
||||
|
||||
// detached user can't know about his phonenumbers
|
||||
$this->assertEquals(0, count($userClone->getPhonenumbers()));
|
||||
|
||||
// detached user can't know about his groups either
|
||||
$this->assertEquals(0, count($userClone->getGroups()));
|
||||
|
||||
### Merge back and flush
|
||||
$userClone = $this->_em->merge($userClone);
|
||||
|
||||
/*
|
||||
* Back in managed world I would expect to have my phonenumbers back but they aren't!
|
||||
* Remember I didn't touch (and propably didn't need) them at all while in detached mode.
|
||||
*/
|
||||
$this->assertEquals(4, count($userClone->getPhonenumbers()), 'Phonenumbers are not available anymore');
|
||||
|
||||
/*
|
||||
* This works fine as long as cmUser::groups doesn't cascade "merge"
|
||||
*/
|
||||
$this->assertEquals(2, count($userClone->getGroups()));
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertFalse($this->_em->contains($userClone));
|
||||
|
||||
### Reload user from DB
|
||||
$userFromEntityManager = $this->loadUserFromEntityManager();
|
||||
|
||||
/*
|
||||
* Strange: Now the phonenumbers are back again
|
||||
*/
|
||||
$this->assertEquals(4, count($userFromEntityManager->getPhonenumbers()));
|
||||
|
||||
/*
|
||||
* This works fine as long as cmUser::groups doesn't cascade "merge"
|
||||
* Otherwise group memberships are physically deleted now!
|
||||
*/
|
||||
$this->assertEquals(2, count($userClone->getGroups()));
|
||||
}
|
||||
|
||||
protected function createAndPersistUser()
|
||||
{
|
||||
$user = new CmsUser();
|
||||
$user->name = 'Luka';
|
||||
$user->username = 'lukacho';
|
||||
$user->status = 'developer';
|
||||
|
||||
foreach(array(1111,2222,3333,4444) as $number) {
|
||||
$phone = new CmsPhonenumber;
|
||||
$phone->phonenumber = $number;
|
||||
$user->addPhonenumber($phone);
|
||||
}
|
||||
|
||||
foreach(array('Moshers', 'Headbangers') as $groupName) {
|
||||
$group = new CmsGroup;
|
||||
$group->setName($groupName);
|
||||
$user->addGroup($group);
|
||||
}
|
||||
|
||||
$this->_em->persist($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Doctrine\Tests\Models\CMS\CmsUser
|
||||
*/
|
||||
protected function loadUserFromEntityManager()
|
||||
{
|
||||
return $this->_em
|
||||
->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name like :name')
|
||||
->setParameter('name', 'Luka')
|
||||
->getSingleResult();
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user