1
0
mirror of synced 2024-12-15 07:36:03 +03:00

Merge pull request #166 from doctrine/DDC-720

DDC-720 - Add support to flush only one entity through EntityManager#flush()
This commit is contained in:
Benjamin Eberlei 2011-10-23 01:07:14 -07:00
commit fd00f2d371
3 changed files with 240 additions and 9 deletions

View File

@ -325,13 +325,17 @@ class EntityManager implements ObjectManager
* This effectively synchronizes the in-memory state of managed objects with the * This effectively synchronizes the in-memory state of managed objects with the
* database. * database.
* *
* If an entity is explicitly passed to this method only this entity and
* the cascade-persist semantics + scheduled inserts/removals are synchronized.
*
* @param object $entity
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that * @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
* makes use of optimistic locking fails. * makes use of optimistic locking fails.
*/ */
public function flush() public function flush($entity = null)
{ {
$this->errorIfClosed(); $this->errorIfClosed();
$this->unitOfWork->commit(); $this->unitOfWork->commit($entity);
} }
/** /**

View File

@ -254,11 +254,17 @@ class UnitOfWork implements PropertyChangedListener
* 4) All collection updates * 4) All collection updates
* 5) All entity deletions * 5) All entity deletions
* *
* @param object $entity
* @return void
*/ */
public function commit() public function commit($entity = null)
{ {
// Compute changes done since last commit. // Compute changes done since last commit.
if ($entity === null) {
$this->computeChangeSets(); $this->computeChangeSets();
} else {
$this->computeSingleEntityChangeSet($entity);
}
if ( ! ($this->entityInsertions || if ( ! ($this->entityInsertions ||
$this->entityDeletions || $this->entityDeletions ||
@ -347,6 +353,61 @@ class UnitOfWork implements PropertyChangedListener
$this->orphanRemovals = array(); $this->orphanRemovals = array();
} }
/**
* Compute the changesets of all entities scheduled for insertion
*
* @return void
*/
private function computeScheduleInsertsChangeSets()
{
foreach ($this->entityInsertions as $entity) {
$class = $this->em->getClassMetadata(get_class($entity));
$this->computeChangeSet($class, $entity);
}
}
/**
* Only flush the given entity according to a ruleset that keeps the UoW consistent.
*
* 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well!
* 2. Read Only entities are skipped.
* 3. Proxies are skipped.
* 4. Only if entity is properly managed.
*
* @param object $entity
* @return void
*/
private function computeSingleEntityChangeSet($entity)
{
if ( ! $this->isInIdentityMap($entity) ) {
throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity));
}
$class = $this->em->getClassMetadata(get_class($entity));
if ($class->isChangeTrackingDeferredImplicit()) {
$this->persist($entity);
}
// Compute changes for INSERTed entities first. This must always happen even in this case.
$this->computeScheduleInsertsChangeSets();
if ( $class->isReadOnly ) {
return;
}
// Ignore uninitialized proxy objects
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
return;
}
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
$oid = spl_object_hash($entity);
if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) {
$this->computeChangeSet($class, $entity);
}
}
/** /**
* Executes any extra updates that have been scheduled. * Executes any extra updates that have been scheduled.
*/ */
@ -526,10 +587,7 @@ class UnitOfWork implements PropertyChangedListener
public function computeChangeSets() public function computeChangeSets()
{ {
// Compute changes for INSERTed entities first. This must always happen. // Compute changes for INSERTed entities first. This must always happen.
foreach ($this->entityInsertions as $entity) { $this->computeScheduleInsertsChangeSets();
$class = $this->em->getClassMetadata(get_class($entity));
$this->computeChangeSet($class, $entity);
}
// Compute changes for other MANAGED entities. Change tracking policies take effect here. // Compute changes for other MANAGED entities. Change tracking policies take effect here.
foreach ($this->identityMap as $className => $entities) { foreach ($this->identityMap as $className => $entities) {

View File

@ -1030,4 +1030,173 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address)); $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address));
} }
/**
* @group DDC-720
*/
public function testFlushSingleManagedEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$user->status = 'administrator';
$this->_em->flush($user);
$this->_em->clear();
$user = $this->_em->find(get_class($user), $user->id);
$this->assertEquals('administrator', $user->status);
}
/**
* @group DDC-720
*/
public function testFlushSingleUnmanagedEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->setExpectedException('InvalidArgumentException', 'Entity has to be managed for single computation');
$this->_em->flush($user);
}
/**
* @group DDC-720
*/
public function testFlushSingleAndNewEntity()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$otherUser = new CmsUser;
$otherUser->name = 'Dominik2';
$otherUser->username = 'domnikl2';
$otherUser->status = 'developer';
$user->status = 'administrator';
$this->_em->persist($otherUser);
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushAndCascadePersist()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$address = new CmsAddress();
$address->city = "Springfield";
$address->zip = "12354";
$address->country = "Germany";
$address->street = "Foo Street";
$address->user = $user;
$user->address = $address;
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($address), "Other user is contained in EntityManager");
$this->assertTrue($address->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushSingleAndNoCascade()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$article1 = new CmsArticle();
$article1->topic = 'Foo';
$article1->text = 'Foo Text';
$article1->author = $user;
$user->articles[] = $article1;
$this->setExpectedException('InvalidArgumentException', "A new entity was found through the relationship 'Doctrine\Tests\Models\CMS\CmsUser#articles'");
$this->_em->flush($user);
}
/**
* @group DDC-720
*/
public function testProxyIsIgnored()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$user = $this->_em->getReference(get_class($user), $user->id);
$otherUser = new CmsUser;
$otherUser->name = 'Dominik2';
$otherUser->username = 'domnikl2';
$otherUser->status = 'developer';
$this->_em->persist($otherUser);
$this->_em->flush($user);
$this->assertTrue($this->_em->contains($otherUser), "Other user is contained in EntityManager");
$this->assertTrue($otherUser->id > 0, "other user has an id");
}
/**
* @group DDC-720
*/
public function testFlushSingleSaveOnlySingle()
{
$user = new CmsUser;
$user->name = 'Dominik';
$user->username = 'domnikl';
$user->status = 'developer';
$this->_em->persist($user);
$user2 = new CmsUser;
$user2->name = 'Dominik';
$user2->username = 'domnikl2';
$user2->status = 'developer';
$this->_em->persist($user2);
$this->_em->flush();
$user->status = 'admin';
$user2->status = 'admin';
$this->_em->flush($user);
$this->_em->clear();
$user2 = $this->_em->find(get_class($user2), $user2->id);
$this->assertEquals('developer', $user2->status);
}
} }