Skip to content

Commit

Permalink
Fixed #DDC-501
Browse files Browse the repository at this point in the history
PersistenCollections had a problem with serializing; they need to be initialized beforehands.
  • Loading branch information
Shurakai committed May 12, 2010
1 parent b63afb6 commit 6c185a2
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 27 deletions.
47 changes: 24 additions & 23 deletions lib/Doctrine/ORM/PersistentCollection.php
Expand Up @@ -94,21 +94,21 @@ final class PersistentCollection implements Collection

/**
* Whether the collection has already been initialized.
*
*
* @var boolean
*/
private $_initialized = true;

/**
* The wrapped Collection instance.
*
*
* @var Collection
*/
private $_coll;

/**
* Creates a new persistent collection.
*
*
* @param EntityManager $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param array The collection elements.
Expand Down Expand Up @@ -145,7 +145,7 @@ public function getOwner()
{
return $this->_owner;
}

public function getTypeClass()
{
return $this->_typeClass;
Expand All @@ -155,7 +155,7 @@ public function getTypeClass()
* INTERNAL:
* Adds an element to a collection during hydration. This will automatically
* complete bidirectional associations in the case of a one-to-many association.
*
*
* @param mixed $element The element to add.
*/
public function hydrateAdd($element)
Expand All @@ -173,7 +173,7 @@ public function hydrateAdd($element)
$this->_owner);
}
}

/**
* INTERNAL:
* Sets a keyed element in the collection during hydration.
Expand Down Expand Up @@ -272,7 +272,7 @@ public function getMapping()
{
return $this->_association;
}

/**
* Marks this collection as changed/dirty.
*/
Expand Down Expand Up @@ -306,17 +306,17 @@ public function setDirty($dirty)
{
$this->_isDirty = $dirty;
}

/**
* Sets the initialized flag of the collection, forcing it into that state.
*
*
* @param boolean $bool
*/
public function setInitialized($bool)
{
$this->_initialized = $bool;
}

/**
* Checks whether this collection has been initialized.
*
Expand Down Expand Up @@ -377,7 +377,7 @@ public function removeElement($element)
$this->_em->getUnitOfWork()->getCollectionPersister($this->_association)
->deleteRows($this, $element);
}*/

$this->_initialize();
$result = $this->_coll->removeElement($element);
$this->_changed();
Expand Down Expand Up @@ -414,7 +414,7 @@ public function contains($element)
}
return false;
}*/

$this->_initialize();
return $this->_coll->contains($element);
}
Expand Down Expand Up @@ -501,7 +501,7 @@ public function isEmpty()
$this->_initialize();
return $this->_coll->isEmpty();
}

/**
* {@inheritdoc}
*/
Expand All @@ -528,7 +528,7 @@ public function filter(Closure $p)
$this->_initialize();
return $this->_coll->filter($p);
}

/**
* {@inheritdoc}
*/
Expand All @@ -546,7 +546,7 @@ public function partition(Closure $p)
$this->_initialize();
return $this->_coll->partition($p);
}

/**
* {@inheritdoc}
*/
Expand All @@ -567,10 +567,10 @@ public function clear()
$this->_changed();
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
}

return $result;
}

/**
* Called by PHP when this collection is serialized. Ensures that only the
* elements are properly serialized.
Expand All @@ -580,9 +580,10 @@ public function clear()
*/
public function __sleep()
{
$this->_initialize();
return array('_coll');
}

/* ArrayAccess implementation */

/**
Expand Down Expand Up @@ -620,28 +621,28 @@ public function offsetUnset($offset)
{
return $this->remove($offset);
}

public function key()
{
return $this->_coll->key();
}

/**
* Gets the element of the collection at the current iterator position.
*/
public function current()
{
return $this->_coll->current();
}

/**
* Moves the internal iterator position to the next element.
*/
public function next()
{
return $this->_coll->next();
}

/**
* Retrieves the wrapped Collection instance.
*/
Expand Down
8 changes: 4 additions & 4 deletions tests/Doctrine/Tests/Models/CMS/CmsUser.php
Expand Up @@ -40,14 +40,14 @@ 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")}
* )
*/
public $groups;

public function __construct() {
$this->phonenumbers = new ArrayCollection;
$this->articles = new ArrayCollection;
Expand Down Expand Up @@ -107,9 +107,9 @@ public function removePhonenumber($index) {
}
return false;
}

public function getAddress() { return $this->address; }

public function setAddress(CmsAddress $address) {
if ($this->address !== $address) {
$this->address = $address;
Expand Down
127 changes: 127 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php
@@ -0,0 +1,127 @@
<?php
namespace Doctrine\Tests\ORM\Functional;

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 DetachedPartiallyLoadedEntityTest 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
$string = serialize($userReloaded);
unset($userReloaded);
$userClone = unserialize($string);
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $userClone);

$this->assertEquals(4, count($userClone->getPhonenumbers()));
$this->assertEquals(2, 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();
}

}

1 comment on commit 6c185a2

@beberlei
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the bugfix? I don't see a relevant change...

Please sign in to comment.