diff --git a/src/Api/CachedIdentifiersExtractor.php b/src/Api/CachedIdentifiersExtractor.php index f053d977491..20bd9951f05 100644 --- a/src/Api/CachedIdentifiersExtractor.php +++ b/src/Api/CachedIdentifiersExtractor.php @@ -33,6 +33,7 @@ final class CachedIdentifiersExtractor implements IdentifiersExtractorInterface private $cacheItemPool; private $propertyAccessor; private $decorated; + private $memoryCache = []; public function __construct(CacheItemPoolInterface $cacheItemPool, IdentifiersExtractorInterface $decorated, PropertyAccessorInterface $propertyAccessor = null) { @@ -46,60 +47,66 @@ public function __construct(CacheItemPoolInterface $cacheItemPool, IdentifiersEx */ public function getIdentifiersFromItem($item): array { - $identifiers = []; - $resourceClass = $this->getObjectClass($item); - - $cacheKey = self::CACHE_KEY_PREFIX.md5($resourceClass); - - // This is to avoid setting the cache twice in the case where the related item cache doesn't exist - $cacheIsHit = false; + $keys = $this->getKeys($item, function ($item) use (&$identifiers) { + return $identifiers = $this->decorated->getIdentifiersFromItem($item); + }); - try { - $cacheItem = $this->cacheItemPool->getItem($cacheKey); - $isRelationCached = true; - - if ($cacheIsHit = $cacheItem->isHit()) { - foreach ($cacheItem->get() as $propertyName) { - $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName); + if (null !== $identifiers) { + return $identifiers; + } - if (!is_object($identifiers[$propertyName])) { - continue; - } + $identifiers = []; + foreach ($keys as $propertyName) { + $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName); - $relatedItem = $identifiers[$propertyName]; - $relatedCacheKey = self::CACHE_KEY_PREFIX.md5($this->getObjectClass($relatedItem)); + if (!is_object($identifiers[$propertyName])) { + continue; + } + $relatedResourceClass = $this->getObjectClass($identifiers[$propertyName]); + if (!$relatedIdentifiers = $this->memoryCache[$relatedResourceClass] ?? false) { + $relatedCacheKey = self::CACHE_KEY_PREFIX.md5($relatedResourceClass); + try { $relatedCacheItem = $this->cacheItemPool->getItem($relatedCacheKey); - if (!$relatedCacheItem->isHit()) { - $isRelationCached = false; - break; + return $this->decorated->getIdentifiersFromItem($item); } + } catch (CacheException $e) { + return $this->decorated->getIdentifiersFromItem($item); + } + + $relatedIdentifiers = $relatedCacheItem->get(); + } - unset($identifiers[$propertyName]); + $identifiers[$propertyName] = $this->propertyAccessor->getValue($identifiers[$propertyName], $relatedIdentifiers[0]); + } - $identifiers[$propertyName] = $this->propertyAccessor->getValue($relatedItem, $relatedCacheItem->get()[0]); - } + return $identifiers; + } - if (true === $isRelationCached) { - return $identifiers; - } + private function getKeys($item, callable $retriever): array + { + $resourceClass = $this->getObjectClass($item); + if (isset($this->memoryCache[$resourceClass])) { + return $this->memoryCache[$resourceClass]; + } + + try { + $cacheItem = $this->cacheItemPool->getItem(self::CACHE_KEY_PREFIX.md5($resourceClass)); + if ($cacheItem->isHit()) { + return $this->memoryCache[$resourceClass] = $cacheItem->get(); } } catch (CacheException $e) { // do nothing } - $identifiers = $this->decorated->getIdentifiersFromItem($item); + $keys = array_keys($retriever($item)); - if (isset($cacheItem) && false === $cacheIsHit) { - try { - $cacheItem->set(array_keys($identifiers)); - $this->cacheItemPool->save($cacheItem); - } catch (CacheException $e) { - // do nothing - } + if (isset($cacheItem)) { + $cacheItem->set($keys); + $this->cacheItemPool->save($cacheItem); } - return $identifiers; + return $this->memoryCache[$resourceClass] = $keys; } } diff --git a/tests/Api/CachedIdentifiersExtractorTest.php b/tests/Api/CachedIdentifiersExtractorTest.php index 78dc2e0aa6f..3a97a11f045 100644 --- a/tests/Api/CachedIdentifiersExtractorTest.php +++ b/tests/Api/CachedIdentifiersExtractorTest.php @@ -45,7 +45,9 @@ public function testFirstPass() $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null); - $this->assertEquals(['id' => 1], $identifiersExtractor->getIdentifiersFromItem($dummy)); + $expectedResult = ['id' => 1]; + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy)); + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy), 'Trigger the local cache'); } public function testSecondPass() @@ -67,7 +69,9 @@ public function testSecondPass() $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null); - $this->assertEquals(['id' => 1], $identifiersExtractor->getIdentifiersFromItem($dummy)); + $expectedResult = ['id' => 1]; + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy)); + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy), 'Trigger the local cache'); } public function testSecondPassWithRelatedNotCached() @@ -98,7 +102,9 @@ public function testSecondPassWithRelatedNotCached() $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null); + $expectedResult = ['id' => 1, 'relatedDummy' => 1]; $this->assertEquals(['id' => 1, 'relatedDummy' => 1], $identifiersExtractor->getIdentifiersFromItem($dummy)); + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy), 'Trigger the local cache'); } public function testSecondPassWithRelatedCached() @@ -130,6 +136,8 @@ public function testSecondPassWithRelatedCached() $identifiersExtractor = new CachedIdentifiersExtractor($cacheItemPool->reveal(), $decoration->reveal(), null); - $this->assertEquals(['id' => 1, 'relatedDummy' => 1], $identifiersExtractor->getIdentifiersFromItem($dummy)); + $expectedResult = ['id' => 1, 'relatedDummy' => 1]; + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy)); + $this->assertEquals($expectedResult, $identifiersExtractor->getIdentifiersFromItem($dummy), 'Trigger the local cache'); } }