Skip to content

Commit 2b296b5

Browse files
committed
fix some tests
1 parent eadf644 commit 2b296b5

File tree

9 files changed

+151
-20
lines changed

9 files changed

+151
-20
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@
165165
"symfony/cache": "^6.4 || ^7.0",
166166
"symfony/config": "^6.4 || ^7.0",
167167
"symfony/console": "^6.4 || ^7.0",
168-
"symfony/object-mapper": "^7.3",
169168
"symfony/css-selector": "^6.4 || ^7.0",
170169
"symfony/dependency-injection": "^6.4 || ^7.0",
171170
"symfony/doctrine-bridge": "^6.4.2 || ^7.1",
@@ -181,6 +180,7 @@
181180
"symfony/maker-bundle": "^1.24",
182181
"symfony/mercure-bundle": "*",
183182
"symfony/messenger": "^6.4 || ^7.0",
183+
"symfony/object-mapper": "^7.3",
184184
"symfony/routing": "^6.4 || ^7.0",
185185
"symfony/security-bundle": "^6.4 || ^7.0",
186186
"symfony/security-core": "^6.4 || ^7.0",

src/State/Processor/ObjectMapperProcessor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ final class ObjectMapperProcessor implements ProcessorInterface
2727
* @param ProcessorInterface<mixed,mixed> $decorated
2828
*/
2929
public function __construct(
30-
private readonly ObjectMapperInterface $objectMapper,
30+
private readonly ?ObjectMapperInterface $objectMapper,
3131
private readonly ProcessorInterface $decorated,
3232
) {
3333
}
3434

3535
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): object|array|null
3636
{
37-
if (!$operation->canWrite()) {
37+
if (!$this->objectMapper || !$operation->canWrite()) {
3838
return $this->decorated->process($data, $operation, $uriVariables, $context);
3939
}
4040

src/State/Provider/ObjectMapperProvider.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Doctrine\Odm\State\Options as OdmOptions;
1717
use ApiPlatform\Doctrine\Orm\State\Options;
1818
use ApiPlatform\Metadata\Operation;
19+
use ApiPlatform\Metadata\Util\CloneTrait;
1920
use ApiPlatform\State\Pagination\ArrayPaginator;
2021
use ApiPlatform\State\Pagination\PaginatorInterface;
2122
use ApiPlatform\State\ProviderInterface;
@@ -27,11 +28,13 @@
2728
*/
2829
final class ObjectMapperProvider implements ProviderInterface
2930
{
31+
use CloneTrait;
32+
3033
/**
31-
* @param ProviderInterface<mixed> $decorated
34+
* @param ProviderInterface<object> $decorated
3235
*/
3336
public function __construct(
34-
private readonly ObjectMapperInterface $objectMapper,
37+
private readonly ?ObjectMapperInterface $objectMapper,
3538
private readonly ProviderInterface $decorated,
3639
) {
3740
}
@@ -40,10 +43,11 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
4043
{
4144
$data = $this->decorated->provide($operation, $uriVariables, $context);
4245

43-
if (!\is_object($data)) {
46+
if (!$this->objectMapper || !\is_object($data)) {
4447
return $data;
4548
}
4649

50+
$request = $context['request'] ?? null;
4751
$entityClass = null;
4852
if (($options = $operation->getStateOptions()) && $options instanceof Options && $options->getEntityClass()) {
4953
$entityClass = $options->getEntityClass();
@@ -53,14 +57,21 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
5357
$entityClass = $options->getDocumentClass();
5458
}
5559

56-
if (!$entityClass || !(new \ReflectionClass($entityClass))->getAttributes(Map::class)) {
60+
$entityClass ??= $data::class;
61+
62+
if (!(new \ReflectionClass($entityClass))->getAttributes(Map::class)) {
5763
return $data;
5864
}
5965

6066
if ($data instanceof PaginatorInterface) {
61-
return new ArrayPaginator(array_map(fn ($v) => $this->objectMapper->map($v), iterator_to_array($data)), 0, \count($data));
67+
$data = new ArrayPaginator(array_map(fn ($v) => $this->objectMapper->map($v), iterator_to_array($data)), 0, \count($data));
68+
} else {
69+
$data = $this->objectMapper->map($data);
6270
}
6371

64-
return $this->objectMapper->map($data);
72+
$request?->attributes->set('data', $data);
73+
$request?->attributes->set('previous_data', $this->clone($data));
74+
75+
return $data;
6576
}
6677
}

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
use Symfony\Component\DependencyInjection\Reference;
6060
use Symfony\Component\Finder\Finder;
6161
use Symfony\Component\HttpClient\ScopingHttpClient;
62-
use Symfony\Component\ObjectMapper\Attribute\Map;
62+
use Symfony\Component\ObjectMapper\ObjectMapper;
6363
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
6464
use Symfony\Component\Uid\AbstractUid;
6565
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -170,7 +170,7 @@ public function load(array $configs, ContainerBuilder $container): void
170170
$this->registerArgumentResolverConfiguration($loader);
171171
$this->registerLinkSecurityConfiguration($loader, $config);
172172

173-
if (class_exists(Map::class)) {
173+
if (class_exists(ObjectMapper::class)) {
174174
$loader->load('state/object_mapper.xml');
175175
}
176176
$container->registerForAutoconfiguration(FilterInterface::class)

src/Symfony/Bundle/Resources/config/state/object_mapper.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
66
<services>
77
<service id="api_platform.state_provider.object_mapper" class="ApiPlatform\State\Provider\ObjectMapperProvider" decorates="api_platform.state_provider.read">
8-
<argument type="service" id="object_mapper" />
8+
<argument type="service" id="object_mapper" on-invalid="null" />
99
<argument type="service" id="api_platform.state_provider.object_mapper.inner" />
1010
</service>
1111

1212
<service id="api_platform.state_processor.object_mapper" class="ApiPlatform\State\Processor\ObjectMapperProcessor" decorates="api_platform.state_processor.locator">
13-
<argument type="service" id="object_mapper" />
13+
<argument type="service" id="object_mapper" on-invalid="null" />
1414
<argument type="service" id="api_platform.state_processor.object_mapper.inner" />
1515
</service>
1616
</services>

tests/Fixtures/TestBundle/ApiResource/MappedResource.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ final class MappedResource
3535

3636
public static function toFirstName(string $v): string
3737
{
38-
return explode(' ', $v)[0] ?? null;
38+
return explode(' ', $v)[0];
3939
}
4040

4141
public static function toLastName(string $v): string
4242
{
43-
return explode(' ', $v)[1] ?? null;
43+
return explode(' ', $v)[1];
4444
}
4545
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;
15+
16+
use ApiPlatform\Doctrine\Odm\State\Options;
17+
use ApiPlatform\JsonLd\ContextBuilder;
18+
use ApiPlatform\Metadata\ApiResource;
19+
use ApiPlatform\Tests\Fixtures\TestBundle\Document\MappedDocument;
20+
use Symfony\Component\ObjectMapper\Attribute\Map;
21+
22+
#[ApiResource(
23+
uriTemplate: 'mapped_resources_odm',
24+
stateOptions: new Options(documentClass: MappedDocument::class),
25+
normalizationContext: [ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX => false],
26+
)]
27+
#[Map(target: MappedDocument::class)]
28+
final class MappedResourceOdm
29+
{
30+
#[Map(if: false)]
31+
public ?string $id = null;
32+
33+
#[Map(target: 'firstName', transform: [self::class, 'toFirstName'])]
34+
#[Map(target: 'lastName', transform: [self::class, 'toLastName'])]
35+
public string $username;
36+
37+
public static function toFirstName(string $v): string
38+
{
39+
return explode(' ', $v)[0];
40+
}
41+
42+
public static function toLastName(string $v): string
43+
{
44+
return explode(' ', $v)[1];
45+
}
46+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Document;
15+
16+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResource;
17+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
18+
use Symfony\Component\ObjectMapper\Attribute\Map;
19+
20+
/**
21+
* MappedEntity to MappedResource.
22+
*/
23+
#[ODM\Document]
24+
#[Map(target: MappedResource::class)]
25+
class MappedDocument
26+
{
27+
#[ODM\Id(strategy: 'INCREMENT', type: 'int')]
28+
private ?int $id = null;
29+
30+
#[ODM\Field]
31+
#[Map(if: false)]
32+
private string $firstName;
33+
34+
#[Map(target: 'username', transform: [self::class, 'toUsername'])]
35+
#[ODM\Field]
36+
private string $lastName;
37+
38+
public static function toUsername($value, $object): string
39+
{
40+
return $object->getFirstName().' '.$object->getLastName();
41+
}
42+
43+
public function getId(): ?int
44+
{
45+
return $this->id;
46+
}
47+
48+
public function setLastName(string $name): void
49+
{
50+
$this->lastName = $name;
51+
}
52+
53+
public function getLastName(): string
54+
{
55+
return $this->lastName;
56+
}
57+
58+
public function setFirstName(string $name): void
59+
{
60+
$this->firstName = $name;
61+
}
62+
63+
public function getFirstName(): string
64+
{
65+
return $this->firstName;
66+
}
67+
}

tests/Functional/MappingTest.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515

1616
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
1717
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResource;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MappedResourceOdm;
19+
use ApiPlatform\Tests\Fixtures\TestBundle\Document\MappedDocument;
1820
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\MappedEntity;
1921
use ApiPlatform\Tests\RecreateSchemaTrait;
2022
use ApiPlatform\Tests\SetupClassResourcesTrait;
23+
use Doctrine\ODM\MongoDB\DocumentManager;
2124

2225
final class MappingTest extends ApiTestCase
2326
{
@@ -30,25 +33,29 @@ final class MappingTest extends ApiTestCase
3033
*/
3134
public static function getResources(): array
3235
{
33-
return [MappedResource::class];
36+
return [MappedResource::class, MappedResourceOdm::class];
3437
}
3538

3639
public function testShouldMapBetweenResourceAndEntity(): void
3740
{
41+
if (!$this->getContainer()->has('object_mapper')) {
42+
$this->markTestSkipped('ObjectMapper not installed');
43+
}
44+
3845
$this->recreateSchema([MappedEntity::class]);
3946
$this->loadFixtures();
40-
self::createClient()->request('GET', 'mapped_resources');
47+
self::createClient()->request('GET', $this->isMongoDB() ? 'mapped_resources_odm' : 'mapped_resources');
4148
$this->assertJsonContains(['member' => [
4249
['username' => 'B0 A0'],
4350
['username' => 'B1 A1'],
4451
['username' => 'B2 A2'],
4552
]]);
4653

47-
$r = self::createClient()->request('POST', 'mapped_resources', ['json' => ['username' => 'so yuka']]);
54+
$r = self::createClient()->request('POST', $this->isMongoDB() ? 'mapped_resources_odm' : 'mapped_resources', ['json' => ['username' => 'so yuka']]);
4855
$this->assertJsonContains(['username' => 'so yuka']);
4956

5057
$manager = $this->getManager();
51-
$repo = $manager->getRepository(MappedEntity::class);
58+
$repo = $manager->getRepository($this->isMongoDB() ? MappedDocument::class : MappedEntity::class);
5259
$persisted = $repo->findOneBy(['id' => $r->toArray()['id']]);
5360
$this->assertSame('so', $persisted->getFirstName());
5461
$this->assertSame('yuka', $persisted->getLastName());
@@ -66,7 +73,7 @@ private function loadFixtures(): void
6673
$manager = $this->getManager();
6774

6875
for ($i = 0; $i < 10; ++$i) {
69-
$e = new MappedEntity();
76+
$e = $manager instanceof DocumentManager ? new MappedDocument() : new MappedEntity();
7077
$e->setLastName('A'.$i);
7178
$e->setFirstName('B'.$i);
7279
$manager->persist($e);

0 commit comments

Comments
 (0)