diff --git a/src/Bundle/Form/DataTransformer/ResourceToIdentifierCacheableTransformer.php b/src/Bundle/Form/DataTransformer/ResourceToIdentifierCacheableTransformer.php new file mode 100644 index 000000000..bd33396f7 --- /dev/null +++ b/src/Bundle/Form/DataTransformer/ResourceToIdentifierCacheableTransformer.php @@ -0,0 +1,86 @@ + */ + private static array $cache; + + public function __construct(RepositoryInterface $repository, ?string $identifier = null) + { + $this->repository = $repository; + $this->identifier = $identifier ?? 'id'; + } + + /** + * @psalm-suppress MissingParamType + * + * @param mixed $value + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + /** @psalm-suppress ArgumentTypeCoercion */ + Assert::isInstanceOf($value, $this->repository->getClassName()); + + return PropertyAccess::createPropertyAccessor()->getValue($value, $this->identifier); + } + + /** @param mixed $value */ + public function reverseTransform($value): ?ResourceInterface + { + if (null === $value) { + return null; + } + + if (isset(self::$cache[$value])) { + return self::$cache[$value]; + } + + /** @var ResourceInterface|null $resource */ + $resource = $this->repository->findOneBy([$this->identifier => $value]); + if (null === $resource) { + throw new TransformationFailedException(sprintf( + 'Object "%s" with identifier "%s"="%s" does not exist.', + $this->repository->getClassName(), + $this->identifier, + $value, + )); + } + + self::$cache[$value] = $resource; + + return $resource; + } + + public function clear(): void + { + self::$cache = []; + } +} diff --git a/src/Bundle/Storage/CookieStorage.php b/src/Bundle/Storage/CookieStorage.php index f259b8e30..e797da11a 100644 --- a/src/Bundle/Storage/CookieStorage.php +++ b/src/Bundle/Storage/CookieStorage.php @@ -44,10 +44,13 @@ public static function getSubscribedEvents(): array public function onKernelRequest(RequestEvent $event): void { + $isMainRequest = false; + /** @phpstan-ignore-next-line */ if (\method_exists($event, 'isMainRequest')) { $isMainRequest = $event->isMainRequest(); - } else { + /** @phpstan-ignore-next-line */ + } elseif (\method_exists($event, 'isMasterRequest')) { $isMainRequest = $event->isMasterRequest(); } if (!$isMainRequest) { @@ -60,10 +63,13 @@ public function onKernelRequest(RequestEvent $event): void public function onKernelResponse(ResponseEvent $event): void { + $isMainRequest = false; + /** @phpstan-ignore-next-line */ if (\method_exists($event, 'isMainRequest')) { $isMainRequest = $event->isMainRequest(); - } else { + /** @phpstan-ignore-next-line */ + } elseif (\method_exists($event, 'isMasterRequest')) { $isMainRequest = $event->isMasterRequest(); } if (!$isMainRequest) { diff --git a/src/Bundle/spec/Form/DataTransformer/ResourceToIdentifierCacheableTransformerSpec.php b/src/Bundle/spec/Form/DataTransformer/ResourceToIdentifierCacheableTransformerSpec.php new file mode 100644 index 000000000..bd956f3e9 --- /dev/null +++ b/src/Bundle/spec/Form/DataTransformer/ResourceToIdentifierCacheableTransformerSpec.php @@ -0,0 +1,81 @@ +beConstructedWith($repository, 'id'); + } + + function it_does_not_reverses_null_value(RepositoryInterface $repository): void + { + $repository->findOneBy(Argument::any())->shouldNotBeCalled(); + + $this->reverseTransform(null)->shouldReturn(null); + } + + function it_throws_an_exception_on_non_existing_resource(RepositoryInterface $repository): void + { + $repository->getClassName()->willReturn(ResourceInterface::class); + $repository->findOneBy(['id' => 6])->willReturn(null); + + $this->shouldThrow(TransformationFailedException::class)->during('reverseTransform', [6]); + } + + function it_reverse_transform_identifier_to_resource(RepositoryInterface $repository, ResourceInterface $resource): void + { + $repository->findOneBy(['id' => 5])->willReturn($resource); + + $this->reverseTransform(5)->shouldReturn($resource); + } + + function it_reverse_transforms_identifier_to_resource_using_cache( + RepositoryInterface $repository, + ResourceInterface $resource, + ): void { + $this->clear(); + + $repository->findOneBy(['id' => 5]) + ->willReturn($resource) + ->shouldBeCalledOnce() + ; + + $this->reverseTransform(5)->shouldReturn($resource); + $this->reverseTransform(5)->shouldReturn($resource); + } + + function it_transforms_null_value_to_empty_string(RepositoryInterface $repository): void + { + $repository->getClassName()->willReturn(ResourceInterface::class); + + $this->transform(null)->shouldReturn(null); + } + + function it_transforms_resource_in_identifier(RepositoryInterface $repository, ResourceInterface $resource): void + { + $repository->getClassName()->willReturn(ResourceInterface::class); + + $resource->getId()->willReturn(6); + + $this->transform($resource)->shouldReturn(6); + } +}