Skip to content

Commit 2297e23

Browse files
committed
(fix)Allow "ID" field even when allow_extra_attributes=false
1 parent 8e68d72 commit 2297e23

File tree

4 files changed

+219
-0
lines changed

4 files changed

+219
-0
lines changed

src/Serializer/ItemNormalizer.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,14 @@ private function getContextUriVariables(array $data, $operation, array $context)
107107

108108
return $uriVariables;
109109
}
110+
111+
protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool
112+
{
113+
$allowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString);
114+
if (\is_array($allowedAttributes) && ($context['api_denormalize'] ?? false)) {
115+
$allowedAttributes = array_merge($allowedAttributes, ['id']);
116+
}
117+
118+
return $allowedAttributes;
119+
}
110120
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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\Entity\Issue6225;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\GetCollection;
19+
use ApiPlatform\Metadata\Link;
20+
use ApiPlatform\Metadata\Post;
21+
use ApiPlatform\Metadata\Put;
22+
use Doctrine\ORM\Mapping as ORM;
23+
use Symfony\Bridge\Doctrine\Types\UuidType;
24+
use Symfony\Component\Serializer\Annotation\Groups;
25+
use Symfony\Component\Uid\Uuid;
26+
27+
#[ORM\Entity]
28+
#[ORM\Table(name: 'bar6225')]
29+
#[ApiResource]
30+
class Bar6225
31+
{
32+
public function __construct()
33+
{
34+
$this->id = Uuid::v7();
35+
}
36+
37+
#[ORM\Id]
38+
#[ORM\Column(type: 'symfony_uuid', unique: true)]
39+
#[Groups(['Foo:Read'])]
40+
private Uuid $id;
41+
42+
#[ORM\OneToOne(mappedBy: 'bar', cascade: ['persist', 'remove'])]
43+
private Foo6225 $foo;
44+
45+
#[ORM\Column(length: 255)]
46+
#[Groups(['Foo:Write', 'Foo:Read'])]
47+
private string $someProperty;
48+
49+
public function getId(): Uuid
50+
{
51+
return $this->id;
52+
}
53+
54+
public function getFoo(): Foo6225
55+
{
56+
return $this->foo;
57+
}
58+
59+
public function setFoo(Foo6225 $foo): static {
60+
$this->foo = $foo;
61+
62+
return $this;
63+
}
64+
65+
public function getSomeProperty(): string
66+
{
67+
return $this->someProperty;
68+
}
69+
70+
public function setSomeProperty(string $someProperty): static {
71+
$this->someProperty = $someProperty;
72+
73+
return $this;
74+
}
75+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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\Entity\Issue6225;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\Patch;
19+
use ApiPlatform\Metadata\Post;
20+
use ApiPlatform\Metadata\Put;
21+
use Doctrine\ORM\Mapping as ORM;
22+
use Symfony\Bridge\Doctrine\Types\UuidType;
23+
use Symfony\Component\Serializer\Annotation\Groups;
24+
use Symfony\Component\Uid\Uuid;
25+
26+
#[ORM\Entity()]
27+
#[ORM\Table(name: 'foo6225')]
28+
#[ApiResource(
29+
operations: [
30+
new Post(),
31+
new Patch(),
32+
],
33+
normalizationContext: [
34+
'groups' => ['Foo:Read'],
35+
],
36+
denormalizationContext: [
37+
'allow_extra_attributes' => false,
38+
'groups' => ['Foo:Write'],
39+
],
40+
)]
41+
class Foo6225
42+
{
43+
public function __construct()
44+
{
45+
$this->id = Uuid::v7();
46+
}
47+
48+
#[ORM\Id]
49+
#[ORM\Column(type: 'symfony_uuid', unique: true)]
50+
#[Groups(['Foo:Read'])]
51+
private Uuid $id;
52+
53+
#[ORM\OneToOne(inversedBy: 'foo', cascade: ['persist', 'remove'])]
54+
#[ORM\JoinColumn(nullable: false)]
55+
#[Groups(['Foo:Write', 'Foo:Read'])]
56+
private Bar6225 $bar;
57+
58+
public function getId(): Uuid
59+
{
60+
return $this->id;
61+
}
62+
63+
public function getBar(): Bar6225
64+
{
65+
return $this->bar;
66+
}
67+
68+
public function setBar(Bar6225 $bar): static
69+
{
70+
$this->bar = $bar;
71+
72+
return $this;
73+
}
74+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace ApiPlatform\Tests\Functional;
4+
5+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
6+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6225\Bar6225;
7+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6225\Foo6225;
8+
use ApiPlatform\Tests\RecreateSchemaTrait;
9+
use ApiPlatform\Tests\SetupClassResourcesTrait;
10+
11+
class NestedPatchTest extends ApiTestCase
12+
{
13+
use RecreateSchemaTrait;
14+
use SetupClassResourcesTrait;
15+
16+
public static function getResources(): array {
17+
return [Foo6225::class, Bar6225::class];
18+
}
19+
20+
public function testIssue6225(): void
21+
{
22+
$this->recreateSchema(self::getResources());
23+
24+
$response = self::createClient()->request('POST', '/foo6225s', [
25+
'json' => [
26+
'bar' => [
27+
'someProperty' => 'abc'
28+
]
29+
],
30+
'headers' => [
31+
'accept' => 'application/json'
32+
]
33+
]);
34+
static::assertResponseIsSuccessful();
35+
$responseContent = json_decode($response->getContent(), true);
36+
$createdFooId = $responseContent['id'];
37+
$createdBarId = $responseContent['bar']['id'];
38+
39+
$patchResponse = self::createClient()->request('PATCH', '/foo6225s/'.$createdFooId, [
40+
'json' => [
41+
'bar' => [
42+
'id' => $createdBarId,
43+
'someProperty' => 'def'
44+
]
45+
],
46+
'headers' => [
47+
'accept' => 'application/json',
48+
'content-type' => 'application/merge-patch+json'
49+
]
50+
]);
51+
static::assertResponseIsSuccessful();
52+
static::assertEquals([
53+
'id' => $createdFooId,
54+
'bar' => [
55+
'id' => $createdBarId,
56+
'someProperty' => 'def'
57+
]
58+
], json_decode($patchResponse->getContent(), true));
59+
}
60+
}

0 commit comments

Comments
 (0)