Skip to content

Commit

Permalink
Merge pull request #1551 from doctrine/DDC-3976
Browse files Browse the repository at this point in the history
[RFC] DDC-3976
  • Loading branch information
guilhermeblanco committed Nov 12, 2015
2 parents 3452f5c + 58992ad commit 3a44a3d
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

/**
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
abstract class AbstractCollectionPersister implements CachedCollectionPersister
Expand Down Expand Up @@ -164,10 +165,18 @@ public function loadCollectionCache(PersistentCollection $collection, Collection
public function storeCollectionCache(CollectionCacheKey $key, $elements)
{
/* @var $targetPersister CachedEntityPersister */
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
$targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);

// Only preserve ordering if association configured it
if ( ! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
// Elements may be an array or a Collection
$elements = array_values(is_array($elements) ? $elements : $elements->getValues());
}

$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);

foreach ($entry->identifiers as $index => $entityKey) {
if ($targetRegion->contains($entityKey)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* Specific non-strict read/write cached entity persister
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
Expand Down Expand Up @@ -77,13 +78,16 @@ public function afterTransactionRolledBack()
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$deleted = $this->persister->delete($entity);

if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}

$this->queuedCache['delete'][] = $key;

return $deleted;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* Specific read-write entity persister
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @since 2.5
*/
class ReadWriteCachedEntityPersister extends AbstractEntityPersister
Expand Down Expand Up @@ -100,21 +101,24 @@ public function afterTransactionRolledBack()
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$deleted = $this->persister->delete($entity);

if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}

if ($lock === null) {
return;
return $deleted;
}

$this->queuedCache['delete'][] = array(
'lock' => $lock,
'key' => $key
);

return $deleted;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ protected function isValidEntityState($entity)

// If Entity is scheduled for inclusion, it is not in this collection.
// We can assure that because it would have return true before on array check
if ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity)) {
return false;
}

return true;
return ! ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity));
}
}
8 changes: 2 additions & 6 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,6 @@ public function computeChangeSet(ClassMetadata $class, $entity)

// Look for changes in associations of the entity
foreach ($class->associationMappings as $field => $assoc) {

if (($val = $class->reflFields[$field]->getValue($entity)) === null) {
continue;
}
Expand Down Expand Up @@ -799,7 +798,7 @@ public function computeChangeSets()
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here.
$oid = spl_object_hash($entity);

if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) {
$this->computeChangeSet($class, $entity);
}
}
Expand All @@ -826,10 +825,7 @@ private function computeAssociationChanges($assoc, $value)
if ($value instanceof PersistentCollection && $value->isDirty()) {
$coid = spl_object_hash($value);

if ($assoc['isOwningSide']) {
$this->collectionUpdates[$coid] = $value;
}

$this->collectionUpdates[$coid] = $value;
$this->visitedCollections[$coid] = $value;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Doctrine/Tests/Models/Cache/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class State
protected $country;

/**
* @Cache
* @Cache("NONSTRICT_READ_WRITE")
* @OneToMany(targetEntity="City", mappedBy="state")
*/
protected $cities;
Expand Down
2 changes: 1 addition & 1 deletion tests/Doctrine/Tests/Models/Cache/Traveler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Traveler
protected $name;

/**
* @Cache()
* @Cache("NONSTRICT_READ_WRITE")
* @OneToMany(targetEntity="Travel", mappedBy="traveler", cascade={"persist", "remove"}, orphanRemoval=true)
*
* @var \Doctrine\Common\Collections\Collection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Doctrine\Tests\ORM\Functional;

use Doctrine\Tests\Models\Cache\City;
use Doctrine\Tests\Models\Cache\ComplexAction;
use Doctrine\Tests\Models\Cache\Country;
use Doctrine\Tests\Models\Cache\State;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\Tests\Models\Cache\Token;
use Doctrine\Tests\Models\Cache\Action;

Expand Down Expand Up @@ -98,6 +98,40 @@ public function testPutAndLoadManyToOneRelation()
$this->assertEquals($this->states[1]->getCountry()->getName(), $c4->getCountry()->getName());
}

public function testInverseSidePutShouldEvictCollection()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();

$this->_em->clear();

$this->cache->evictEntityRegion(State::CLASSNAME);
$this->cache->evictEntityRegion(Country::CLASSNAME);

//evict collection on add
$c3 = $this->_em->find(State::CLASSNAME, $this->states[0]->getId());
$prev = $c3->getCities();
$count = $prev->count();
$city = new City("Buenos Aires", $c3);

$c3->addCity($city);

$this->_em->persist($city);
$this->_em->persist($c3);
$this->_em->flush();
$this->_em->clear();

$state = $this->_em->find(State::CLASSNAME, $c3->getId());
$queryCount = $this->getCurrentQueryCount();

// Association was cleared from EM
$this->assertNotEquals($prev, $state->getCities());

// New association has one more item (cache was evicted)
$this->assertEquals($count + 1, $state->getCities()->count());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
}

public function testShouldNotReloadWhenAssociationIsMissing()
{
$this->loadFixturesCountries();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
*/
class SecondLevelCacheOneToManyTest extends SecondLevelCacheAbstractTest
{
public function testShouldNotPutCollectionInverseSideOnPersist()
public function testShouldPutCollectionInverseSideOnPersist()
{
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();

$this->_em->clear();

$this->assertTrue($this->cache->containsEntity(State::CLASSNAME, $this->states[0]->getId()));
$this->assertTrue($this->cache->containsEntity(State::CLASSNAME, $this->states[1]->getId()));

$this->assertFalse($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[0]->getId()));
$this->assertFalse($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[1]->getId()));
$this->assertTrue($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[0]->getId()));
$this->assertTrue($this->cache->containsCollection(State::CLASSNAME, 'cities', $this->states[1]->getId()));
}

public function testPutAndLoadOneToManyRelation()
Expand Down Expand Up @@ -187,6 +187,7 @@ public function testOneToManyRemove()
$this->loadFixturesCountries();
$this->loadFixturesStates();
$this->loadFixturesCities();

$this->_em->clear();
$this->secondLevelCacheLogger->clearStats();

Expand Down Expand Up @@ -247,8 +248,8 @@ public function testOneToManyRemove()
$this->_em->remove($city0);
$this->_em->persist($state);
$this->_em->flush();

$this->_em->clear();

$this->secondLevelCacheLogger->clearStats();

$queryCount = $this->getCurrentQueryCount();
Expand All @@ -261,29 +262,29 @@ public function testOneToManyRemove()
$this->assertInstanceOf(City::CLASSNAME, $city1);
$this->assertEquals($entity->getCities()->get(1)->getName(), $city1->getName());

$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::CLASSNAME)));
$this->assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));

$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());

$state->getCities()->remove(0);

$this->_em->remove($city1);
$this->_em->persist($state);
$this->_em->flush();

$this->_em->clear();

$this->secondLevelCacheLogger->clearStats();

$queryCount = $this->getCurrentQueryCount();
$state = $this->_em->find(State::CLASSNAME, $this->states[0]->getId());

$this->assertCount(0, $state->getCities());

$this->assertEquals(1, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(2, $this->secondLevelCacheLogger->getHitCount());
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getEntityRegion(State::CLASSNAME)));
$this->assertEquals(0, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
$this->assertEquals(1, $this->secondLevelCacheLogger->getRegionHitCount($this->getCollectionRegion(State::CLASSNAME, 'cities')));
}

public function testOneToManyWithEmptyRelation()
Expand Down Expand Up @@ -346,11 +347,12 @@ public function testOneToManyCount()
public function testCacheInitializeCollectionWithNewObjects()
{
$this->_em->clear();

$this->evictRegions();

$traveler = new Traveler("Doctrine Bot");

for ($i=0; $i<3; ++$i) {
for ($i = 0; $i < 3; ++$i) {
$traveler->getTravels()->add(new Travel($traveler));
}

Expand All @@ -373,7 +375,7 @@ public function testCacheInitializeCollectionWithNewObjects()
$this->assertFalse($entity->getTravels()->isInitialized());
$this->assertCount(4, $entity->getTravels());
$this->assertTrue($entity->getTravels()->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());

$this->_em->flush();
$this->_em->clear();
Expand Down

0 comments on commit 3a44a3d

Please sign in to comment.