From 69c006ec4ec9d5e648b7df7c24468720fd074422 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Thu, 17 Oct 2024 13:54:05 +0200 Subject: [PATCH] Add Single Media Property Resolver and displayOption handling (#3) --- .../CategorySelectionPropertyResolverTest.php | 2 +- .../MediaSelectionPropertyResolver.php | 19 ++- .../SingleMediaSelectionPropertyResolver.php | 59 ++++++++ .../MediaSelectionPropertyResolverTest.php | 130 +++++++++++++++++ ...ngleMediaSelectionPropertyResolverTest.php | 131 ++++++++++++++++++ .../MediaResourceLoaderTest.php | 72 ++++++++++ 6 files changed, 408 insertions(+), 5 deletions(-) rename src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/{Resolver => }/MediaSelectionPropertyResolver.php (68%) create mode 100644 src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolver.php create mode 100644 src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolverTest.php create mode 100644 src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolverTest.php create mode 100644 src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/ResourceLoader/MediaResourceLoaderTest.php diff --git a/src/Sulu/Bundle/CategoryBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/CategorySelectionPropertyResolverTest.php b/src/Sulu/Bundle/CategoryBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/CategorySelectionPropertyResolverTest.php index b4800c2284a..d8c58b70277 100644 --- a/src/Sulu/Bundle/CategoryBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/CategorySelectionPropertyResolverTest.php +++ b/src/Sulu/Bundle/CategoryBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/CategorySelectionPropertyResolverTest.php @@ -14,8 +14,8 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ResolvableResource; use Sulu\Bundle\CategoryBundle\Infrastructure\Sulu\Content\PropertyResolver\CategorySelectionPropertyResolver; +use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ResolvableResource; #[CoversClass(CategorySelectionPropertyResolver::class)] class CategorySelectionPropertyResolverTest extends TestCase diff --git a/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/Resolver/MediaSelectionPropertyResolver.php b/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolver.php similarity index 68% rename from src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/Resolver/MediaSelectionPropertyResolver.php rename to src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolver.php index f2ca467710f..c3086c190e0 100644 --- a/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/Resolver/MediaSelectionPropertyResolver.php +++ b/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolver.php @@ -11,7 +11,7 @@ * with this source code in the file LICENSE. */ -namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\PropertyResolver\Resolver; +namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\PropertyResolver; use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ContentView; use Sulu\Bundle\ContentBundle\Content\Application\PropertyResolver\PropertyResolverInterface; @@ -26,8 +26,15 @@ class MediaSelectionPropertyResolver implements PropertyResolverInterface { public function resolve(mixed $data, string $locale, array $params = []): ContentView { - if (empty($data) || !\is_array($data) || !isset($data['ids'])) { - return ContentView::create([], ['ids' => []]); + $displayOption = (\is_array($data) && isset($data['displayOption']) && \is_string($data['displayOption'])) + ? $data['displayOption'] + : null; + + if (!\is_array($data) + || !isset($data['ids']) + || !\array_is_list($data['ids']) + ) { + return ContentView::create([], ['ids' => [], 'displayOption' => $displayOption, ...$params]); } /** @var string $resourceLoaderKey */ @@ -36,7 +43,11 @@ public function resolve(mixed $data, string $locale, array $params = []): Conten return ContentView::createResolvables( $data['ids'], $resourceLoaderKey, - ['ids' => $data['ids']], + [ + 'ids' => $data['ids'], + 'displayOption' => $displayOption, + ...$params, + ], ); } diff --git a/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolver.php b/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolver.php new file mode 100644 index 00000000000..4ed8c72221d --- /dev/null +++ b/src/Sulu/Bundle/MediaBundle/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolver.php @@ -0,0 +1,59 @@ + null, 'displayOption' => $displayOption, ...$params]); + } + + /** @var string $resourceLoaderKey */ + $resourceLoaderKey = $params['resourceLoader'] ?? MediaResourceLoader::getKey(); + $id = (int) $data['id']; + + return ContentView::createResolvable( + $id, + $resourceLoaderKey, + [ + 'id' => $id, + 'displayOption' => $displayOption, + ...$params, + ], + ); + } + + public static function getType(): string + { + return 'single_media_selection'; + } +} diff --git a/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolverTest.php b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolverTest.php new file mode 100644 index 00000000000..8512f98fe1a --- /dev/null +++ b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/MediaSelectionPropertyResolverTest.php @@ -0,0 +1,130 @@ +resolver = new MediaSelectionPropertyResolver(); + } + + public function testResolveEmpty(): void + { + $contentView = $this->resolver->resolve([], 'en'); + + $this->assertSame([], $contentView->getContent()); + $this->assertSame(['ids' => [], 'displayOption' => null], $contentView->getView()); + } + + public function testResolveParams(): void + { + $contentView = $this->resolver->resolve([], 'en', ['custom' => 'params']); + + $this->assertSame([], $contentView->getContent()); + $this->assertSame([ + 'ids' => [], + 'displayOption' => null, + 'custom' => 'params', + ], $contentView->getView()); + } + + #[DataProvider('provideUnresolvableData')] + public function testResolveUnresolvableData(mixed $data, ?string $expectedDisplayOption = null): void + { + $contentView = $this->resolver->resolve($data, 'en'); + + $this->assertSame([], $contentView->getContent()); + $this->assertSame(['ids' => [], 'displayOption' => $expectedDisplayOption], $contentView->getView()); + } + + /** + * @return iterable + */ + public static function provideUnresolvableData(): iterable + { + yield 'null' => [null]; + yield 'smart_content' => [['source' => '123']]; + yield 'single_value' => [1]; + yield 'object' => [(object) [1, 2]]; + yield 'int_list_not_in_ids' => [[1, 2]]; + yield 'ids_null' => [['ids' => null]]; + yield 'display_option_only' => [['displayOption' => 'left'], 'left']; + } + + /** + * @param array{ + * ids?: array, + * displayOption?: string|null, + * } $data + */ + #[DataProvider('provideResolvableData')] + public function testResolveResolvableData(array $data, ?string $expectedDisplayOption = null): void + { + $contentView = $this->resolver->resolve($data, 'en'); + + $content = $contentView->getContent(); + $this->assertIsArray($content); + + foreach (($data['ids'] ?? []) as $key => $value) { + $resolvable = $content[$key] ?? null; + $this->assertInstanceOf(ResolvableResource::class, $resolvable); + $this->assertSame($value, $resolvable->getId()); + $this->assertSame('media', $resolvable->getResourceLoaderKey()); + } + + $this->assertSame([ + 'ids' => ($data['ids'] ?? []), + 'displayOption' => $expectedDisplayOption, + ], $contentView->getView()); + } + + /** + * @return iterable, + * displayOption?: string|null, + * }, + * }> + */ + public static function provideResolvableData(): iterable + { + yield 'empty' => [[]]; + yield 'int_list' => [['ids' => [1, 2]]]; + yield 'int_list_with_display_option' => [['ids' => [1, 2], 'displayOption' => 'left'], 'left']; + yield 'string_list' => [['ids' => ['1', '2']]]; + yield 'string_list_with_display_option' => [['ids' => ['1', '2'], 'displayOption' => 'left'], 'left']; + } + + public function testCustomResourceLoader(): void + { + $contentView = $this->resolver->resolve(['ids' => [1]], 'en', ['resourceLoader' => 'custom_media']); + + $content = $contentView->getContent(); + $this->assertIsArray($content); + $resolvable = $content[0] ?? null; + $this->assertInstanceOf(ResolvableResource::class, $resolvable); + $this->assertSame(1, $resolvable->getId()); + $this->assertSame('custom_media', $resolvable->getResourceLoaderKey()); + } +} diff --git a/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolverTest.php b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolverTest.php new file mode 100644 index 00000000000..4ee680dd9f2 --- /dev/null +++ b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/PropertyResolver/SingleMediaSelectionPropertyResolverTest.php @@ -0,0 +1,131 @@ +resolver = new SingleMediaSelectionPropertyResolver(); + } + + public function testResolveEmpty(): void + { + $contentView = $this->resolver->resolve(null, 'en'); + + $this->assertSame(null, $contentView->getContent()); + $this->assertSame(['id' => null, 'displayOption' => null], $contentView->getView()); + } + + public function testResolveParams(): void + { + $contentView = $this->resolver->resolve(null, 'en', ['custom' => 'params']); + + $this->assertSame(null, $contentView->getContent()); + $this->assertSame([ + 'id' => null, + 'displayOption' => null, + 'custom' => 'params', + ], $contentView->getView()); + } + + #[DataProvider('provideUnresolvableData')] + public function testResolveUnresolvableData(mixed $data, ?string $expectedDisplayOption = null): void + { + $contentView = $this->resolver->resolve($data, 'en'); + + $this->assertSame(null, $contentView->getContent()); + $this->assertSame(['id' => null, 'displayOption' => $expectedDisplayOption], $contentView->getView()); + } + + /** + * @return iterable + */ + public static function provideUnresolvableData(): iterable + { + yield 'null' => [null]; + yield 'smart_content' => [['source' => '123']]; + yield 'single_value' => [1]; + yield 'object' => [(object) [1, 2]]; + yield 'int_list_not_in_ids' => [[1, 2]]; + yield 'ids_null' => [['ids' => null]]; + yield 'ids_list' => [['ids' => [1, 2]]]; + yield 'id_list' => [['id' => [1, 2]]]; + yield 'display_option_only' => [['displayOption' => 'left'], 'left']; + } + + /** + * @param array{ + * id?: string|int, + * displayOption?: string|null, + * } $data + */ + #[DataProvider('provideResolvableData')] + public function testResolveResolvableData(array $data, ?string $expectedDisplayOption = null): void + { + $contentView = $this->resolver->resolve($data, 'en'); + + $content = $contentView->getContent(); + $id = $data['id'] ?? null; + if (null !== $id) { + $id = (int) $id; + $this->assertInstanceOf(ResolvableResource::class, $content); + $this->assertSame($id, $content->getId()); + $this->assertSame('media', $content->getResourceLoaderKey()); + } + + $this->assertSame([ + 'id' => $id, + 'displayOption' => $expectedDisplayOption, + ], $contentView->getView()); + } + + /** + * @return iterable + */ + public static function provideResolvableData(): iterable + { + yield 'empty' => [[]]; + yield 'int_id' => [['id' => 1]]; + yield 'int_id_with_display_option' => [['id' => 1, 'displayOption' => 'left'], 'left']; + yield 'string_id' => [['id' => '1']]; + yield 'string_id_with_display_option' => [['id' => '1', 'displayOption' => 'left'], 'left']; + } + + public function testCustomResourceLoader(): void + { + $contentView = $this->resolver->resolve(['ids' => [1]], 'en', ['resourceLoader' => 'custom_media']); + + $content = $contentView->getContent(); + $this->assertIsArray($content); + $resolvable = $content[0] ?? null; + $this->assertInstanceOf(ResolvableResource::class, $resolvable); + $this->assertSame(1, $resolvable->getId()); + $this->assertSame('custom_media', $resolvable->getResourceLoaderKey()); + } +} diff --git a/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/ResourceLoader/MediaResourceLoaderTest.php b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/ResourceLoader/MediaResourceLoaderTest.php new file mode 100644 index 00000000000..4395683fcf3 --- /dev/null +++ b/src/Sulu/Bundle/MediaBundle/Tests/Unit/Infrastructure/Sulu/Content/ResourceLoader/MediaResourceLoaderTest.php @@ -0,0 +1,72 @@ + + */ + private ObjectProphecy $mediaManager; + + private MediaResourceLoader $loader; + + public function setUp(): void + { + $this->mediaManager = $this->prophesize(MediaManagerInterface::class); + $this->loader = new MediaResourceLoader($this->mediaManager->reveal()); + } + + public function testGetKey(): void + { + $this->assertSame('media', $this->loader::getKey()); + } + + public function testLoad(): void + { + $media1 = $this->createMedia(1); + $media2 = $this->createMedia(3); + + $this->mediaManager->getByIds([1, 3], 'en')->willReturn([ + $media1, + $media2, + ]) + ->shouldBeCalled(); + + $result = $this->loader->load([1, 3], 'en', []); + + $this->assertSame([ + 1 => $media1, + 3 => $media2, + ], $result); + } + + private static function createMedia(int $id): ApiMedia + { + $media = new Media(); + static::setPrivateProperty($media, 'id', $id); + + return new ApiMedia($media, 'en'); + } +}