From e9ec0a24da69a1fb2934904c2d50b45f80dccb3c Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Thu, 6 Mar 2014 19:42:43 -0500 Subject: [PATCH] [SLC] resolve association cache entry --- .../ORM/Cache/DefaultCollectionHydrator.php | 2 +- .../ORM/Cache/DefaultEntityHydrator.php | 2 +- lib/Doctrine/ORM/Cache/DefaultQueryCache.php | 17 +--- lib/Doctrine/ORM/Cache/EntityCacheEntry.php | 20 ++++ .../SecondLevelCacheQueryCacheTest.php | 94 ++++++++++++++++++- 5 files changed, 120 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php b/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php index 72409509d..a74b88dfb 100644 --- a/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php +++ b/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php @@ -89,7 +89,7 @@ class DefaultCollectionHydrator implements CollectionHydrator return null; } - $list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints); + $list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); } array_walk($list, function($entity, $index) use ($collection) { diff --git a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php index 6d9ac53b8..6f22900e1 100644 --- a/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php +++ b/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php @@ -144,7 +144,7 @@ class DefaultEntityHydrator implements EntityHydrator return null; } - $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->data, $hints); + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), $hints); } if ($entity !== null) { diff --git a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php index a6b833d27..927d46ebe 100644 --- a/lib/Doctrine/ORM/Cache/DefaultQueryCache.php +++ b/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -108,7 +108,6 @@ class DefaultQueryCache implements QueryCache $entityName = reset($rsm->aliasMap); $hasRelation = ( ! empty($rsm->relationMap)); $persister = $this->uow->getEntityPersister($entityName); - $metadata = ( ! $hasRelation) ? $persister->getClassMetadata() : null; $region = $persister->getCacheRegion(); $regionName = $region->getName(); @@ -127,22 +126,16 @@ class DefaultQueryCache implements QueryCache if ($this->cacheLogger !== null) { $this->cacheLogger->entityCacheHit($regionName, $entityKey); } - $data = $entityEntry->data; if ( ! $hasRelation) { - foreach ($metadata->associationMappings as $name => $assoc) { - if ( ! ($assoc['type'] & ClassMetadata::TO_ONE) || ! isset($assoc['cache']) || ! isset($data[$name])) { - continue; - } - $data[$name] = $this->em->getReference($data[$name]->class, $data[$name]->identifier); - } - - $result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints); + $result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); continue; } + $data = $entityEntry->data; + foreach ($entry['associations'] as $name => $assoc) { $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); @@ -159,7 +152,7 @@ class DefaultQueryCache implements QueryCache return null; } - $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->data, self::$hints); + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); if ($this->cacheLogger !== null) { $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); @@ -186,7 +179,7 @@ class DefaultQueryCache implements QueryCache return null; } - $element = $this->uow->createEntity($assocEntry->class, $assocEntry->data, self::$hints); + $element = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); $collection->hydrateSet($assocIndex, $element); diff --git a/lib/Doctrine/ORM/Cache/EntityCacheEntry.php b/lib/Doctrine/ORM/Cache/EntityCacheEntry.php index 03d3c1f24..ece7fae18 100644 --- a/lib/Doctrine/ORM/Cache/EntityCacheEntry.php +++ b/lib/Doctrine/ORM/Cache/EntityCacheEntry.php @@ -20,6 +20,8 @@ namespace Doctrine\ORM\Cache; +use Doctrine\ORM\EntityManagerInterface; + /** * Entity cache entry * @@ -63,4 +65,22 @@ class EntityCacheEntry implements CacheEntry { return new self($values['class'], $values['data']); } + + /** + * Retrieves the entity data resolving cache entries + * + * @param \Doctrine\ORM\EntityManagerInterfac $em + * + * @return array + */ + public function resolveAssociationEntries(EntityManagerInterface $em) + { + return array_map(function($value) use ($em) { + if ( ! ($value instanceof AssociationCacheEntry)) { + return $value; + } + + return $em->getReference($value->class, $value->identifier); + }, $this->data); + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php index 3f0aa3896..995c1d07f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SecondLevelCacheQueryCacheTest.php @@ -827,7 +827,7 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest $this->assertEquals(1, $this->secondLevelCacheLogger->getRegionMissCount('bar_region')); } - public function testToOneAssociationShouldUseProxyForCacheableMetaResultColumn() + public function testResolveAssociationCacheEntry() { $this->evictRegions(); $this->loadFixturesCountries(); @@ -875,6 +875,98 @@ class SecondLevelCacheQueryCacheTest extends SecondLevelCacheAbstractTest $this->assertEquals($stateId, $state2->getId()); } + public function testResolveToOneAssociationCacheEntry() + { + $this->evictRegions(); + $this->loadFixturesCountries(); + $this->loadFixturesStates(); + $this->loadFixturesCities(); + $this->evictRegions(); + + $this->_em->clear(); + + $cityId = $this->cities[0]->getId(); + $dql = 'SELECT c, s FROM Doctrine\Tests\Models\Cache\City c JOIN c.state s WHERE c.id = :id'; + $query = $this->_em->createQuery($dql); + $queryCount = $this->getCurrentQueryCount(); + + $query1 = clone $query; + $city1 = $query1 + ->setParameter('id', $cityId) + ->setCacheable(true) + ->setMaxResults(1) + ->getSingleResult(); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $city1); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $city1->getState()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $city1->getState()->getCities()->get(0)); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $city1->getState()->getCities()->get(0)->getState()); + + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + $query2 = clone $query; + $city2 = $query2 + ->setParameter('id', $cityId) + ->setCacheable(true) + ->setMaxResults(1) + ->getSingleResult(); + + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $city2); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $city2->getState()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $city2->getState()->getCities()->get(0)); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $city2->getState()->getCities()->get(0)->getState()); + } + + public function testResolveToManyAssociationCacheEntry() + { + $this->evictRegions(); + $this->loadFixturesCountries(); + $this->loadFixturesStates(); + $this->loadFixturesCities(); + $this->evictRegions(); + + $this->_em->clear(); + + $stateId = $this->states[0]->getId(); + $dql = 'SELECT s, c FROM Doctrine\Tests\Models\Cache\State s JOIN s.cities c WHERE s.id = :id'; + $query = $this->_em->createQuery($dql); + $queryCount = $this->getCurrentQueryCount(); + + $query1 = clone $query; + $state1 = $query1 + ->setParameter('id', $stateId) + ->setCacheable(true) + ->setMaxResults(1) + ->getSingleResult(); + + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state1); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $state1->getCountry()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $state1->getCities()->get(0)); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state1->getCities()->get(0)->getState()); + $this->assertSame($state1, $state1->getCities()->get(0)->getState()); + + $this->_em->clear(); + + $queryCount = $this->getCurrentQueryCount(); + $query2 = clone $query; + $state2 = $query2 + ->setParameter('id', $stateId) + ->setCacheable(true) + ->setMaxResults(1) + ->getSingleResult(); + + $this->assertEquals($queryCount, $this->getCurrentQueryCount()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state2); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $state2->getCountry()); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $state2->getCities()->get(0)); + $this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state2->getCities()->get(0)->getState()); + $this->assertSame($state2, $state2->getCities()->get(0)->getState()); + } + public function testHintClearEntityRegionUpdateStatement() { $this->evictRegions();