From 0b2582276910c153a3c240b434b59e8e95cfc40f Mon Sep 17 00:00:00 2001 From: Sandro Hopf <5164586+senaria@users.noreply.github.com> Date: Fri, 28 Nov 2025 14:42:40 +0100 Subject: [PATCH 1/3] fix(state): handle non-paginated collections in ObjectMapperProvider --- src/State/Provider/ObjectMapperProvider.php | 8 +- .../ApiResource/Issue7563/BookDto.php | 41 +++ .../IsbnToCustomIsbnTransformer.php | 29 +++ tests/Fixtures/app/config/config_common.yml | 4 + .../State/Provider/Issue7563Test.php | 107 ++++++++ .../Provider/ObjectMapperProviderTest.php | 244 ++++++++++++++++++ 6 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue7563/BookDto.php create mode 100644 tests/Fixtures/TestBundle/ObjectMapper/IsbnToCustomIsbnTransformer.php create mode 100644 tests/Functional/State/Provider/Issue7563Test.php create mode 100644 tests/State/Provider/ObjectMapperProviderTest.php diff --git a/src/State/Provider/ObjectMapperProvider.php b/src/State/Provider/ObjectMapperProvider.php index 89f1c332fce..6504b8fbb70 100644 --- a/src/State/Provider/ObjectMapperProvider.php +++ b/src/State/Provider/ObjectMapperProvider.php @@ -40,7 +40,11 @@ public function provide(Operation $operation, array $uriVariables = [], array $c { $data = $this->decorated->provide($operation, $uriVariables, $context); - if (!$this->objectMapper || !\is_object($data) || !$operation->canMap()) { + if (!$this->objectMapper || !$operation->canMap()) { + return $data; + } + + if (!\is_object($data) && !\is_array($data)) { return $data; } @@ -49,6 +53,8 @@ public function provide(Operation $operation, array $uriVariables = [], array $c if ($data instanceof PaginatorInterface) { $data = new ArrayPaginator(array_map(fn ($v) => $this->objectMapper->map($v, $operation->getClass()), iterator_to_array($data)), 0, \count($data)); + } elseif (\is_array($data)) { + $data = array_map(fn ($v) => $this->objectMapper->map($v, $operation->getClass()), $data); } else { $data = $this->objectMapper->map($data, $operation->getClass()); } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue7563/BookDto.php b/tests/Fixtures/TestBundle/ApiResource/Issue7563/BookDto.php new file mode 100644 index 00000000000..63b83bfa8c2 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue7563/BookDto.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563; + +use ApiPlatform\Doctrine\Orm\State\Options; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Book; +use ApiPlatform\Tests\Fixtures\TestBundle\ObjectMapper\IsbnToCustomIsbnTransformer; +use Symfony\Component\ObjectMapper\Attribute\Map; + +#[Get( + stateOptions: new Options(entityClass: Book::class) +)] +#[GetCollection( + stateOptions: new Options(entityClass: Book::class) +)] +#[Map(source: Book::class)] +class BookDto +{ + public function __construct( + #[Map(source: 'id')] + public ?int $id = null, + #[Map(source: 'name')] + public ?string $name = null, + #[Map(source: 'isbn', transform: IsbnToCustomIsbnTransformer::class)] + public ?string $customIsbn = null, + ) { + } +} diff --git a/tests/Fixtures/TestBundle/ObjectMapper/IsbnToCustomIsbnTransformer.php b/tests/Fixtures/TestBundle/ObjectMapper/IsbnToCustomIsbnTransformer.php new file mode 100644 index 00000000000..8372483dac8 --- /dev/null +++ b/tests/Fixtures/TestBundle/ObjectMapper/IsbnToCustomIsbnTransformer.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ObjectMapper; + +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Book; +use Symfony\Component\ObjectMapper\TransformCallableInterface; + +/** + * @implements TransformCallableInterface + */ +final readonly class IsbnToCustomIsbnTransformer implements TransformCallableInterface +{ + public function __invoke(mixed $value, object $source, ?object $target): mixed + { + return 'custom'.$value; + } +} diff --git a/tests/Fixtures/app/config/config_common.yml b/tests/Fixtures/app/config/config_common.yml index f47386188b4..eb58c616ae8 100644 --- a/tests/Fixtures/app/config/config_common.yml +++ b/tests/Fixtures/app/config/config_common.yml @@ -271,6 +271,10 @@ services: tags: - name: 'api_platform.state_processor' + ApiPlatform\Tests\Fixtures\TestBundle\ObjectMapper\IsbnToCustomIsbnTransformer: + tags: + - name: 'object_mapper.transform_callable' + app.messenger_handler.messenger_with_response: class: 'ApiPlatform\Tests\Fixtures\TestBundle\MessengerHandler\MessengerWithResponseHandler' tags: diff --git a/tests/Functional/State/Provider/Issue7563Test.php b/tests/Functional/State/Provider/Issue7563Test.php new file mode 100644 index 00000000000..e66382351a9 --- /dev/null +++ b/tests/Functional/State/Provider/Issue7563Test.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Functional\State\Provider; + +use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Book; +use ApiPlatform\Tests\RecreateSchemaTrait; +use ApiPlatform\Tests\SetupClassResourcesTrait; + +/** + * Tests for ObjectMapperProvider to verify object mapping works correctly + * for single items, paginated collections, and unpaginated collections. + */ +final class Issue7563Test extends ApiTestCase +{ + use RecreateSchemaTrait; + use SetupClassResourcesTrait; + + protected static ?bool $alwaysBootKernel = false; + + /** + * @return class-string[] + */ + public static function getResources(): array + { + return [BookDto::class]; + } + + protected function setUp(): void + { + parent::setUp(); + + $this->recreateSchema([Book::class]); + $this->loadFixtures(); + } + + private function loadFixtures(): void + { + $manager = $this->getManager(); + + for ($i = 1; $i <= 5; ++$i) { + $book = new Book(); + $book->name = 'Book '.$i; + $book->isbn = 'ISBN-'.$i; + $manager->persist($book); + } + + $manager->flush(); + } + + public function testGetSingleBookDto(): void + { + // When + self::createClient()->request('GET', '/book_dtos/1'); + + // Then + self::assertResponseIsSuccessful(); + self::assertJsonContains([ + '@type' => 'BookDto', + 'id' => 1, + 'name' => 'Book 1', + 'customIsbn' => 'customISBN-1', + ]); + } + + public function testGetCollectionBookDtoPaginated(): void + { + // When + $response = self::createClient()->request('GET', '/book_dtos'); + + // Then + self::assertResponseIsSuccessful(); + + $json = $response->toArray(); + self::assertCount(3, $json['hydra:member']); + foreach ($response->toArray()['hydra:member'] as $member) { + self::assertStringStartsWith('customISBN-', $member['customIsbn']); + } + } + + public function testGetCollectionBookDtoUnpaginated(): void + { + // When + $response = self::createClient()->request('GET', '/book_dtos?pagination=false'); + + // Then + self::assertResponseIsSuccessful(); + + $json = $response->toArray(); + self::assertCount(5, $json['hydra:member']); + foreach ($json['hydra:member'] as $member) { + self::assertStringStartsWith('customISBN-', $member['customIsbn']); + } + } +} diff --git a/tests/State/Provider/ObjectMapperProviderTest.php b/tests/State/Provider/ObjectMapperProviderTest.php new file mode 100644 index 00000000000..bdd1884af19 --- /dev/null +++ b/tests/State/Provider/ObjectMapperProviderTest.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\State\Provider; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\State\Pagination\ArrayPaginator; +use ApiPlatform\State\Provider\ObjectMapperProvider; +use ApiPlatform\State\ProviderInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\ObjectMapper\ObjectMapperInterface; + +class ObjectMapperProviderTest extends TestCase +{ + public function testProvideBypassesWhenNoObjectMapper(): void + { + // Given + $data = new SourceEntity(); + $operation = new Get(class: TargetResource::class); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($data); + $provider = new ObjectMapperProvider(null, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertSame($data, $result); + } + + public function testProvideBypassesWhenOperationCannotMap(): void + { + // Given + $data = new SourceEntity(); + $operation = new Get(class: TargetResource::class, map: false); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->never())->method('map'); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($data); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertSame($data, $result); + } + + public function testProvideBypassesWhenDataIsNull(): void + { + // Given + $operation = new Get(class: TargetResource::class, map: true); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->never())->method('map'); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn(null); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertNull($result); + } + + public function testProvideMapsObject(): void + { + // Given + $sourceEntity = new SourceEntity(); + $targetResource = new TargetResource(); + $operation = new Get(class: TargetResource::class, map: true); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->once()) + ->method('map') + ->with($sourceEntity, TargetResource::class) + ->willReturn($targetResource); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($sourceEntity); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertSame($targetResource, $result); + } + + public function testProvideMapsObjectAndSetsRequestAttributes(): void + { + // Given + $sourceEntity = new SourceEntity(); + $targetResource = new TargetResource(); + $operation = new Get(class: TargetResource::class, map: true); + $request = new Request(); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->once()) + ->method('map') + ->with($sourceEntity, TargetResource::class) + ->willReturn($targetResource); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($sourceEntity); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation, [], ['request' => $request]); + + // Then + $this->assertSame($targetResource, $result); + $this->assertSame($sourceEntity, $request->attributes->get('mapped_data')); + $this->assertSame($targetResource, $request->attributes->get('data')); + $this->assertInstanceOf(TargetResource::class, $request->attributes->get('previous_data')); + $this->assertNotSame($targetResource, $request->attributes->get('previous_data')); + } + + public function testProvideMapsArray(): void + { + // Given + $sourceEntity1 = new SourceEntity(); + $sourceEntity2 = new SourceEntity(); + $targetResource1 = new TargetResource(); + $targetResource2 = new TargetResource(); + $operation = new Get(class: TargetResource::class, map: true); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->exactly(2)) + ->method('map') + ->willReturnCallback(function ($source, $target) use ($sourceEntity1, $sourceEntity2, $targetResource1, $targetResource2) { + if ($source === $sourceEntity1) { + return $targetResource1; + } + if ($source === $sourceEntity2) { + return $targetResource2; + } + + return null; + }); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn([$sourceEntity1, $sourceEntity2]); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertIsArray($result); + $this->assertCount(2, $result); + $this->assertSame($targetResource1, $result[0]); + $this->assertSame($targetResource2, $result[1]); + } + + public function testProvideMapsPaginator(): void + { + // Given + $sourceEntity1 = new SourceEntity(); + $sourceEntity2 = new SourceEntity(); + $targetResource1 = new TargetResource(); + $targetResource2 = new TargetResource(); + $operation = new Get(class: TargetResource::class, map: true); + $paginator = new ArrayPaginator([$sourceEntity1, $sourceEntity2], 0, 10); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->exactly(2)) + ->method('map') + ->willReturnCallback(function ($source, $target) use ($sourceEntity1, $sourceEntity2, $targetResource1, $targetResource2) { + if ($source === $sourceEntity1) { + return $targetResource1; + } + if ($source === $sourceEntity2) { + return $targetResource2; + } + + return null; + }); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($paginator); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertInstanceOf(ArrayPaginator::class, $result); + $items = iterator_to_array($result); + $this->assertCount(2, $items); + $this->assertSame($targetResource1, $items[0]); + $this->assertSame($targetResource2, $items[1]); + } + + public function testProvideMapsEmptyArray(): void + { + // Given + $operation = new Get(class: TargetResource::class, map: true); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->never())->method('map'); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn([]); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertIsArray($result); + $this->assertEmpty($result); + } + + public function testProvideMapsEmptyPaginator(): void + { + // Given + $operation = new Get(class: TargetResource::class, map: true); + $paginator = new ArrayPaginator([], 0, 10); + $objectMapper = $this->createMock(ObjectMapperInterface::class); + $objectMapper->expects($this->never())->method('map'); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn($paginator); + $provider = new ObjectMapperProvider($objectMapper, $decorated); + + // When + $result = $provider->provide($operation); + + // Then + $this->assertInstanceOf(ArrayPaginator::class, $result); + $this->assertCount(0, iterator_to_array($result)); + } +} + +class SourceEntity +{ + public string $name = 'source'; +} + +class TargetResource +{ + public string $name = 'target'; +} From c1eebc11212933e8d6d8e3988aee698d268308da Mon Sep 17 00:00:00 2001 From: soyuka Date: Sun, 30 Nov 2025 17:24:20 +0100 Subject: [PATCH 2/3] merge test + ingore mongodb --- tests/Functional/MappingTest.php | 60 ++++++++++ .../State/Provider/Issue7563Test.php | 107 ------------------ 2 files changed, 60 insertions(+), 107 deletions(-) delete mode 100644 tests/Functional/State/Provider/Issue7563Test.php diff --git a/tests/Functional/MappingTest.php b/tests/Functional/MappingTest.php index 232298b0209..85f6c04dd70 100644 --- a/tests/Functional/MappingTest.php +++ b/tests/Functional/MappingTest.php @@ -17,6 +17,7 @@ use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\FirstResource; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResource; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceNoMap; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceOdm; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceSourceOnly; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceWithInput; @@ -24,6 +25,7 @@ use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceWithRelationRelated; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\SecondResource; use ApiPlatform\Tests\Fixtures\TestBundle\Document\MappedDocument; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Book; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MappedEntity; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MappedEntityNoMap; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MappedEntitySourceOnly; @@ -55,6 +57,7 @@ public static function getResources(): array MappedResourceWithInput::class, MappedResourceSourceOnly::class, MappedResourceNoMap::class, + BookDto::class, ]; } @@ -277,4 +280,61 @@ private function loadFixtures(): void $manager->flush(); } + + public function testGetSingleBookDto(): void + { + if ($this->isMongoDB()) { + $this->markTestSkipped('MongoDB not tested.'); + } + + // When + self::createClient()->request('GET', '/book_dtos/1'); + + // Then + self::assertResponseIsSuccessful(); + self::assertJsonContains([ + '@type' => 'BookDto', + 'id' => 1, + 'name' => 'Book 1', + 'customIsbn' => 'customISBN-1', + ]); + } + + public function testGetCollectionBookDtoPaginated(): void + { + if ($this->isMongoDB()) { + $this->markTestSkipped('MongoDB not tested.'); + } + + // When + $response = self::createClient()->request('GET', '/book_dtos'); + + // Then + self::assertResponseIsSuccessful(); + + $json = $response->toArray(); + self::assertCount(3, $json['hydra:member']); + foreach ($response->toArray()['hydra:member'] as $member) { + self::assertStringStartsWith('customISBN-', $member['customIsbn']); + } + } + + public function testGetCollectionBookDtoUnpaginated(): void + { + if ($this->isMongoDB()) { + $this->markTestSkipped('MongoDB not tested.'); + } + + // When + $response = self::createClient()->request('GET', '/book_dtos?pagination=false'); + + // Then + self::assertResponseIsSuccessful(); + + $json = $response->toArray(); + self::assertCount(5, $json['hydra:member']); + foreach ($json['hydra:member'] as $member) { + self::assertStringStartsWith('customISBN-', $member['customIsbn']); + } + } } diff --git a/tests/Functional/State/Provider/Issue7563Test.php b/tests/Functional/State/Provider/Issue7563Test.php deleted file mode 100644 index e66382351a9..00000000000 --- a/tests/Functional/State/Provider/Issue7563Test.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace ApiPlatform\Tests\Functional\State\Provider; - -use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; -use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; -use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Book; -use ApiPlatform\Tests\RecreateSchemaTrait; -use ApiPlatform\Tests\SetupClassResourcesTrait; - -/** - * Tests for ObjectMapperProvider to verify object mapping works correctly - * for single items, paginated collections, and unpaginated collections. - */ -final class Issue7563Test extends ApiTestCase -{ - use RecreateSchemaTrait; - use SetupClassResourcesTrait; - - protected static ?bool $alwaysBootKernel = false; - - /** - * @return class-string[] - */ - public static function getResources(): array - { - return [BookDto::class]; - } - - protected function setUp(): void - { - parent::setUp(); - - $this->recreateSchema([Book::class]); - $this->loadFixtures(); - } - - private function loadFixtures(): void - { - $manager = $this->getManager(); - - for ($i = 1; $i <= 5; ++$i) { - $book = new Book(); - $book->name = 'Book '.$i; - $book->isbn = 'ISBN-'.$i; - $manager->persist($book); - } - - $manager->flush(); - } - - public function testGetSingleBookDto(): void - { - // When - self::createClient()->request('GET', '/book_dtos/1'); - - // Then - self::assertResponseIsSuccessful(); - self::assertJsonContains([ - '@type' => 'BookDto', - 'id' => 1, - 'name' => 'Book 1', - 'customIsbn' => 'customISBN-1', - ]); - } - - public function testGetCollectionBookDtoPaginated(): void - { - // When - $response = self::createClient()->request('GET', '/book_dtos'); - - // Then - self::assertResponseIsSuccessful(); - - $json = $response->toArray(); - self::assertCount(3, $json['hydra:member']); - foreach ($response->toArray()['hydra:member'] as $member) { - self::assertStringStartsWith('customISBN-', $member['customIsbn']); - } - } - - public function testGetCollectionBookDtoUnpaginated(): void - { - // When - $response = self::createClient()->request('GET', '/book_dtos?pagination=false'); - - // Then - self::assertResponseIsSuccessful(); - - $json = $response->toArray(); - self::assertCount(5, $json['hydra:member']); - foreach ($json['hydra:member'] as $member) { - self::assertStringStartsWith('customISBN-', $member['customIsbn']); - } - } -} From b359494ef9d03284bad879056d087e3f5b9c01a7 Mon Sep 17 00:00:00 2001 From: soyuka Date: Sun, 30 Nov 2025 17:24:45 +0100 Subject: [PATCH 3/3] fixes --- src/State/Processor/ObjectMapperProcessor.php | 2 +- tests/Functional/MappingTest.php | 34 ++++++++++++------ .../Provider/ObjectMapperProviderTest.php | 36 ------------------- 3 files changed, 25 insertions(+), 47 deletions(-) diff --git a/src/State/Processor/ObjectMapperProcessor.php b/src/State/Processor/ObjectMapperProcessor.php index 3f0074d744f..2f542d276d7 100644 --- a/src/State/Processor/ObjectMapperProcessor.php +++ b/src/State/Processor/ObjectMapperProcessor.php @@ -15,8 +15,8 @@ use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; -use Symfony\Component\ObjectMapper\ObjectMapperInterface; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\ObjectMapper\ObjectMapperInterface; /** * @implements ProcessorInterface diff --git a/tests/Functional/MappingTest.php b/tests/Functional/MappingTest.php index 85f6c04dd70..d697ae202d2 100644 --- a/tests/Functional/MappingTest.php +++ b/tests/Functional/MappingTest.php @@ -15,9 +15,9 @@ use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\FirstResource; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResource; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceNoMap; -use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue7563\BookDto; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceOdm; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceSourceOnly; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceWithInput; @@ -281,16 +281,30 @@ private function loadFixtures(): void $manager->flush(); } + private function loadBookFixtures(): void + { + $manager = $this->getManager(); + + for ($i = 1; $i <= 5; ++$i) { + $book = new Book(); + $book->name = 'Book '.$i; + $book->isbn = 'ISBN-'.$i; + $manager->persist($book); + } + + $manager->flush(); + } + public function testGetSingleBookDto(): void { if ($this->isMongoDB()) { $this->markTestSkipped('MongoDB not tested.'); } - // When - self::createClient()->request('GET', '/book_dtos/1'); + $this->recreateSchema([Book::class]); + $this->loadBookFixtures(); - // Then + self::createClient()->request('GET', '/book_dtos/1'); self::assertResponseIsSuccessful(); self::assertJsonContains([ '@type' => 'BookDto', @@ -306,10 +320,10 @@ public function testGetCollectionBookDtoPaginated(): void $this->markTestSkipped('MongoDB not tested.'); } - // When - $response = self::createClient()->request('GET', '/book_dtos'); + $this->recreateSchema([Book::class]); + $this->loadBookFixtures(); - // Then + $response = self::createClient()->request('GET', '/book_dtos'); self::assertResponseIsSuccessful(); $json = $response->toArray(); @@ -325,10 +339,10 @@ public function testGetCollectionBookDtoUnpaginated(): void $this->markTestSkipped('MongoDB not tested.'); } - // When - $response = self::createClient()->request('GET', '/book_dtos?pagination=false'); + $this->recreateSchema([Book::class]); + $this->loadBookFixtures(); - // Then + $response = self::createClient()->request('GET', '/book_dtos?pagination=false'); self::assertResponseIsSuccessful(); $json = $response->toArray(); diff --git a/tests/State/Provider/ObjectMapperProviderTest.php b/tests/State/Provider/ObjectMapperProviderTest.php index bdd1884af19..8b5b3d456dd 100644 --- a/tests/State/Provider/ObjectMapperProviderTest.php +++ b/tests/State/Provider/ObjectMapperProviderTest.php @@ -25,23 +25,18 @@ class ObjectMapperProviderTest extends TestCase { public function testProvideBypassesWhenNoObjectMapper(): void { - // Given $data = new SourceEntity(); $operation = new Get(class: TargetResource::class); $decorated = $this->createStub(ProviderInterface::class); $decorated->method('provide')->willReturn($data); $provider = new ObjectMapperProvider(null, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertSame($data, $result); } public function testProvideBypassesWhenOperationCannotMap(): void { - // Given $data = new SourceEntity(); $operation = new Get(class: TargetResource::class, map: false); $objectMapper = $this->createMock(ObjectMapperInterface::class); @@ -50,16 +45,12 @@ public function testProvideBypassesWhenOperationCannotMap(): void $decorated->method('provide')->willReturn($data); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertSame($data, $result); } public function testProvideBypassesWhenDataIsNull(): void { - // Given $operation = new Get(class: TargetResource::class, map: true); $objectMapper = $this->createMock(ObjectMapperInterface::class); $objectMapper->expects($this->never())->method('map'); @@ -67,16 +58,12 @@ public function testProvideBypassesWhenDataIsNull(): void $decorated->method('provide')->willReturn(null); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertNull($result); } public function testProvideMapsObject(): void { - // Given $sourceEntity = new SourceEntity(); $targetResource = new TargetResource(); $operation = new Get(class: TargetResource::class, map: true); @@ -89,16 +76,12 @@ public function testProvideMapsObject(): void $decorated->method('provide')->willReturn($sourceEntity); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertSame($targetResource, $result); } public function testProvideMapsObjectAndSetsRequestAttributes(): void { - // Given $sourceEntity = new SourceEntity(); $targetResource = new TargetResource(); $operation = new Get(class: TargetResource::class, map: true); @@ -112,10 +95,7 @@ public function testProvideMapsObjectAndSetsRequestAttributes(): void $decorated->method('provide')->willReturn($sourceEntity); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation, [], ['request' => $request]); - - // Then $this->assertSame($targetResource, $result); $this->assertSame($sourceEntity, $request->attributes->get('mapped_data')); $this->assertSame($targetResource, $request->attributes->get('data')); @@ -125,7 +105,6 @@ public function testProvideMapsObjectAndSetsRequestAttributes(): void public function testProvideMapsArray(): void { - // Given $sourceEntity1 = new SourceEntity(); $sourceEntity2 = new SourceEntity(); $targetResource1 = new TargetResource(); @@ -148,10 +127,7 @@ public function testProvideMapsArray(): void $decorated->method('provide')->willReturn([$sourceEntity1, $sourceEntity2]); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertIsArray($result); $this->assertCount(2, $result); $this->assertSame($targetResource1, $result[0]); @@ -160,7 +136,6 @@ public function testProvideMapsArray(): void public function testProvideMapsPaginator(): void { - // Given $sourceEntity1 = new SourceEntity(); $sourceEntity2 = new SourceEntity(); $targetResource1 = new TargetResource(); @@ -184,10 +159,7 @@ public function testProvideMapsPaginator(): void $decorated->method('provide')->willReturn($paginator); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertInstanceOf(ArrayPaginator::class, $result); $items = iterator_to_array($result); $this->assertCount(2, $items); @@ -197,7 +169,6 @@ public function testProvideMapsPaginator(): void public function testProvideMapsEmptyArray(): void { - // Given $operation = new Get(class: TargetResource::class, map: true); $objectMapper = $this->createMock(ObjectMapperInterface::class); $objectMapper->expects($this->never())->method('map'); @@ -205,17 +176,13 @@ public function testProvideMapsEmptyArray(): void $decorated->method('provide')->willReturn([]); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertIsArray($result); $this->assertEmpty($result); } public function testProvideMapsEmptyPaginator(): void { - // Given $operation = new Get(class: TargetResource::class, map: true); $paginator = new ArrayPaginator([], 0, 10); $objectMapper = $this->createMock(ObjectMapperInterface::class); @@ -224,10 +191,7 @@ public function testProvideMapsEmptyPaginator(): void $decorated->method('provide')->willReturn($paginator); $provider = new ObjectMapperProvider($objectMapper, $decorated); - // When $result = $provider->provide($operation); - - // Then $this->assertInstanceOf(ArrayPaginator::class, $result); $this->assertCount(0, iterator_to_array($result)); }