diff --git a/CHANGELOG.md b/CHANGELOG.md index 24430da..16a19d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * refactor: Move mapping logic in `ObjectToObjectTransformer` to its own service. * refactor: Move more mapping logic to `ObjectMappingResolver`. +* perf: Add caching for `ObjectMappingResolver`. ## 0.5.12 diff --git a/config/services.php b/config/services.php index 65a83a7..d72bdb4 100644 --- a/config/services.php +++ b/config/services.php @@ -28,6 +28,7 @@ use Rekalogika\Mapper\Transformer\DateTimeTransformer; use Rekalogika\Mapper\Transformer\InheritanceMapTransformer; use Rekalogika\Mapper\Transformer\NullTransformer; +use Rekalogika\Mapper\Transformer\ObjectMappingResolver\CachingObjectMappingResolver; use Rekalogika\Mapper\Transformer\ObjectMappingResolver\ObjectMappingResolver; use Rekalogika\Mapper\Transformer\ObjectToArrayTransformer; use Rekalogika\Mapper\Transformer\ObjectToObjectTransformer; @@ -195,6 +196,19 @@ service('rekalogika.mapper.property_info'), ]); + $services + ->set('rekalogika.mapper.cache.object_mapping_resolver') + ->parent('cache.system') + ->tag('cache.pool'); + + $services + ->set('rekalogika.mapper.object_mapping_resolver.cache', CachingObjectMappingResolver::class) + ->decorate('rekalogika.mapper.object_mapping_resolver') + ->args([ + service('.inner'), + service('rekalogika.mapper.cache.object_mapping_resolver') + ]); + # transformer registry $services diff --git a/src/Transformer/ObjectMappingResolver/CachingObjectMappingResolver.php b/src/Transformer/ObjectMappingResolver/CachingObjectMappingResolver.php new file mode 100644 index 0000000..abf2d01 --- /dev/null +++ b/src/Transformer/ObjectMappingResolver/CachingObjectMappingResolver.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Transformer\ObjectMappingResolver; + +use Psr\Cache\CacheItemPoolInterface; +use Rekalogika\Mapper\Context\Context; +use Rekalogika\Mapper\Transformer\ObjectMappingResolver\Contracts\ObjectMapping; +use Rekalogika\Mapper\Transformer\ObjectMappingResolver\Contracts\ObjectMappingResolverInterface; + +final class CachingObjectMappingResolver implements ObjectMappingResolverInterface +{ + /** + * @var array + */ + private array $cache = []; + + public function __construct( + private ObjectMappingResolverInterface $decorated, + private CacheItemPoolInterface $cacheItemPool, + ) { + } + + public function resolveObjectMapping( + string $sourceClass, + string $targetClass, + Context $context + ): ObjectMapping { + $cacheKey = $sourceClass . ':' . $targetClass; + + if (isset($this->cache[$cacheKey])) { + return $this->cache[$cacheKey]; + } + + $cacheItem = $this->cacheItemPool->getItem($cacheKey); + + if ($cacheItem->isHit()) { + /** @var mixed */ + $cached = $cacheItem->get(); + + if ($cached instanceof ObjectMapping) { + return $this->cache[$cacheKey] = $cached; + } + + unset($this->cache[$cacheKey]); + $this->cacheItemPool->deleteItem($cacheKey); + } + + $objectMapping = $this->decorated + ->resolveObjectMapping($sourceClass, $targetClass, $context); + + $cacheItem->set($objectMapping); + $this->cacheItemPool->save($cacheItem); + + return $this->cache[$cacheKey] = $objectMapping; + } +}