Skip to content

Commit f596383

Browse files
authored
Merge pull request #6060 from soyuka/merge-32b
Merge 3.1
2 parents dcce751 + 4eae88b commit f596383

File tree

10 files changed

+252
-43
lines changed

10 files changed

+252
-43
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ Notes:
228228
* [92a81f024](https://github.com/api-platform/core/commit/92a81f024541054b9322e7457b75c721261e14e0) feat(graphql): allow to disable the introspection query (#5711)
229229
* [d793ffb92](https://github.com/api-platform/core/commit/d793ffb9228a21655ee35f0b90a959f93281a4cf) feat: union/intersect types (#5470)
230230

231+
## v3.1.24
232+
233+
### Bug fixes
234+
235+
* [9660a190a](https://github.com/api-platform/core/commit/9660a190a264102f7d1cfa1eae41f397ec559391) fix(serializer): concat context on wrong id (#6050)
236+
* [a9a06897b](https://github.com/api-platform/core/commit/a9a06897b38a1b7a68db0e217d659d2dc5450851) fix(doctrine): get reference with identifier value (#6019)
237+
* [cc16a1ced](https://github.com/api-platform/core/commit/cc16a1ced3300a6080fbc428bade0291ca5bcb82) fix(jsonschema): iri example (#5901)
238+
231239
## v3.1.23
232240

233241
### Bug fixes

features/main/standard_put.feature

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,48 @@ Feature: Spec-compliant PUT support
4747
"bar": ""
4848
}
4949
"""
50+
51+
@createSchema
52+
@!mongodb
53+
Scenario: Create a new resource identified by an uid
54+
When I add "Content-Type" header equal to "application/ld+json"
55+
And I send a "PUT" request to "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335" with body:
56+
"""
57+
{
58+
"name": "test"
59+
}
60+
"""
61+
Then the response status code should be 201
62+
And the response should be in JSON
63+
And the JSON should be equal to:
64+
"""
65+
{
66+
"@context": "/contexts/UidIdentified",
67+
"@id": "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335",
68+
"@type": "UidIdentified",
69+
"id": "fbcf5910-d915-4f7d-ba39-6b2957c57335",
70+
"name": "test"
71+
}
72+
"""
73+
74+
@!mongodb
75+
Scenario: Replace an existing resource
76+
When I add "Content-Type" header equal to "application/ld+json"
77+
And I send a "PUT" request to "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335" with body:
78+
"""
79+
{
80+
"name": "bar"
81+
}
82+
"""
83+
Then the response status code should be 200
84+
And the response should be in JSON
85+
And the JSON should be equal to:
86+
"""
87+
{
88+
"@context": "/contexts/UidIdentified",
89+
"@id": "/uid_identifieds/fbcf5910-d915-4f7d-ba39-6b2957c57335",
90+
"@type": "UidIdentified",
91+
"id": "fbcf5910-d915-4f7d-ba39-6b2957c57335",
92+
"name": "bar"
93+
}
94+
"""

features/openapi/docs.feature

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ Feature: Documentation support
100100
{
101101
"owl:maxCardinality": 1,
102102
"type": "string",
103-
"format": "iri-reference"
103+
"format": "iri-reference",
104+
"example": "https://example.com/"
104105
}
105106
"""
106107
# Enable these tests when SF 4.4 / PHP 7.1 support is dropped

src/Doctrine/Common/State/PersistProcessor.php

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,46 +50,46 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
5050
// https://github.com/doctrine/orm/issues/8461#issuecomment-1250233555
5151
if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false)) {
5252
\assert(method_exists($manager, 'getReference'));
53-
// TODO: the call to getReference is most likely to fail with complex identifiers
5453
$newData = $data;
55-
if ($previousData = $context['previous_data']) {
56-
$newData = 1 === \count($uriVariables) ? $manager->getReference($class, current($uriVariables)) : clone $previousData;
57-
}
58-
5954
$identifiers = array_reverse($uriVariables);
6055
$links = $this->getLinks($class, $operation, $context);
6156
$reflectionProperties = $this->getReflectionProperties($data);
6257

63-
if (!$previousData) {
64-
foreach (array_reverse($links) as $link) {
65-
if ($link->getExpandedValue() || !$link->getFromClass()) {
58+
// TODO: the call to getReference is most likely to fail with complex identifiers
59+
if ($previousData = $context['previous_data']) {
60+
$classMetadata = $manager->getClassMetadata($class);
61+
$identifiers = $classMetadata->getIdentifierValues($previousData);
62+
$newData = 1 === \count($identifiers) ? $manager->getReference($class, current($identifiers)) : clone $previousData;
63+
64+
foreach ($reflectionProperties as $propertyName => $reflectionProperty) {
65+
// // Don't override the property if it's part of the subresource system
66+
if (isset($identifiers[$propertyName]) || isset($uriVariables[$propertyName])) {
6667
continue;
6768
}
6869

69-
$identifierProperties = $link->getIdentifiers();
70-
$hasCompositeIdentifiers = 1 < \count($identifierProperties);
70+
// Skip URI variables as sometime an uri variable is not the doctrine identifier
71+
foreach ($links as $link) {
72+
if (\in_array($propertyName, $link->getIdentifiers(), true)) {
73+
continue 2;
74+
}
75+
}
7176

72-
foreach ($identifierProperties as $identifierProperty) {
73-
$reflectionProperty = $reflectionProperties[$identifierProperty];
74-
$reflectionProperty->setValue($newData, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
77+
if (($newValue = $reflectionProperty->getValue($data)) !== $reflectionProperty->getValue($newData)) {
78+
$reflectionProperty->setValue($newData, $newValue);
7579
}
7680
}
7781
} else {
78-
foreach ($reflectionProperties as $propertyName => $reflectionProperty) {
79-
// Don't override the property if it's part of the subresource system
80-
if (isset($uriVariables[$propertyName])) {
82+
foreach (array_reverse($links) as $link) {
83+
if ($link->getExpandedValue() || !$link->getFromClass()) {
8184
continue;
8285
}
8386

84-
foreach ($links as $link) {
85-
$identifierProperties = $link->getIdentifiers();
86-
if (\in_array($propertyName, $identifierProperties, true)) {
87-
continue;
88-
}
87+
$identifierProperties = $link->getIdentifiers();
88+
$hasCompositeIdentifiers = 1 < \count($identifierProperties);
8989

90-
if (($newValue = $reflectionProperty->getValue($data)) !== $reflectionProperty->getValue($newData)) {
91-
$reflectionProperty->setValue($newData, $newValue);
92-
}
90+
foreach ($identifierProperties as $identifierProperty) {
91+
$reflectionProperty = $reflectionProperties[$identifierProperty];
92+
$reflectionProperty->setValue($newData, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
9393
}
9494
}
9595
}
@@ -113,7 +113,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
113113
private function isDeferredExplicit(DoctrineObjectManager $manager, $data): bool
114114
{
115115
$classMetadata = $manager->getClassMetadata($this->getObjectClass($data));
116-
if (method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) {
116+
if ($classMetadata && method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) { // @phpstan-ignore-line metadata can be null
117117
return $classMetadata->isChangeTrackingDeferredExplicit();
118118
}
119119

src/JsonSchema/Metadata/Property/Factory/SchemaPropertyMetadataFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ private function getClassType(?string $className, bool $nullable, ?bool $readabl
260260
return [
261261
'type' => 'string',
262262
'format' => 'iri-reference',
263+
'example' => 'https://example.com/',
263264
];
264265
}
265266

src/JsonSchema/Tests/TypeFactoryTest.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ public static function typeProvider(): iterable
5757
yield [['nullable' => true, 'type' => 'string', 'format' => 'date-time'], new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTimeImmutable::class)];
5858
yield [['type' => 'string', 'format' => 'duration'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateInterval::class)];
5959
yield [['type' => 'string', 'format' => 'binary'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \SplFileInfo::class)];
60-
yield [['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
61-
yield [['nullable' => true, 'type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
60+
yield [['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
61+
yield [['nullable' => true, 'type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
6262
yield ['enum' => ['type' => 'string', 'enum' => ['male', 'female']], new Type(Type::BUILTIN_TYPE_OBJECT, false, GenderTypeEnum::class)];
6363
yield ['nullable enum' => ['type' => 'string', 'enum' => ['male', 'female', null], 'nullable' => true], new Type(Type::BUILTIN_TYPE_OBJECT, true, GenderTypeEnum::class)];
64-
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
65-
yield ['nullable enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'nullable' => true], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
64+
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
65+
yield ['nullable enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/', 'nullable' => true], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
6666
yield [['type' => 'array', 'items' => ['type' => 'string']], new Type(Type::BUILTIN_TYPE_STRING, false, null, true)];
6767
yield 'array can be itself nullable' => [
6868
['nullable' => true, 'type' => 'array', 'items' => ['type' => 'string']],
@@ -185,12 +185,12 @@ public static function jsonSchemaTypeProvider(): iterable
185185
yield [['type' => ['string', 'null'], 'format' => 'date-time'], new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTimeImmutable::class)];
186186
yield [['type' => 'string', 'format' => 'duration'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateInterval::class)];
187187
yield [['type' => 'string', 'format' => 'binary'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \SplFileInfo::class)];
188-
yield [['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
189-
yield [['type' => ['string', 'null'], 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
188+
yield [['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
189+
yield [['type' => ['string', 'null'], 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
190190
yield ['enum' => ['type' => 'string', 'enum' => ['male', 'female']], new Type(Type::BUILTIN_TYPE_OBJECT, false, GenderTypeEnum::class)];
191191
yield ['nullable enum' => ['type' => ['string', 'null'], 'enum' => ['male', 'female', null]], new Type(Type::BUILTIN_TYPE_OBJECT, true, GenderTypeEnum::class)];
192-
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
193-
yield ['nullable enum resource' => ['type' => ['string', 'null'], 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
192+
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
193+
yield ['nullable enum resource' => ['type' => ['string', 'null'], 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
194194
yield [['type' => 'array', 'items' => ['type' => 'string']], new Type(Type::BUILTIN_TYPE_STRING, false, null, true)];
195195
yield 'array can be itself nullable' => [
196196
['type' => ['array', 'null'], 'items' => ['type' => 'string']],
@@ -306,12 +306,12 @@ public static function openAPIV2TypeProvider(): iterable
306306
yield [['type' => 'string', 'format' => 'date-time'], new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTimeImmutable::class)];
307307
yield [['type' => 'string', 'format' => 'duration'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateInterval::class)];
308308
yield [['type' => 'string', 'format' => 'binary'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \SplFileInfo::class)];
309-
yield [['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
310-
yield [['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
309+
yield [['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
310+
yield [['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
311311
yield ['enum' => ['type' => 'string', 'enum' => ['male', 'female']], new Type(Type::BUILTIN_TYPE_OBJECT, false, GenderTypeEnum::class)];
312312
yield ['nullable enum' => ['type' => 'string', 'enum' => ['male', 'female', null]], new Type(Type::BUILTIN_TYPE_OBJECT, true, GenderTypeEnum::class)];
313-
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
314-
yield ['nullable enum resource' => ['type' => 'string', 'format' => 'iri-reference'], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
313+
yield ['enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, false, GamePlayMode::class)];
314+
yield ['nullable enum resource' => ['type' => 'string', 'format' => 'iri-reference', 'example' => 'https://example.com/'], new Type(Type::BUILTIN_TYPE_OBJECT, true, GamePlayMode::class)];
315315
yield [['type' => 'array', 'items' => ['type' => 'string']], new Type(Type::BUILTIN_TYPE_STRING, false, null, true)];
316316
yield 'array can be itself nullable, but ignored in OpenAPI V2' => [
317317
['type' => 'array', 'items' => ['type' => 'string']],

src/JsonSchema/TypeFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ private function getClassType(?string $className, bool $nullable, string $format
147147
return [
148148
'type' => 'string',
149149
'format' => 'iri-reference',
150+
'example' => 'https://example.com/',
150151
];
151152
}
152153

src/Serializer/ItemNormalizer.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,20 @@ private function updateObjectToPopulate(array $data, array &$context): void
7777
try {
7878
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri((string) $data['id'], $context + ['fetch_data' => true]);
7979
} catch (LegacyInvalidArgumentException|InvalidArgumentException) {
80-
$operation = $this->resourceMetadataCollectionFactory->create($context['resource_class'])->getOperation();
80+
$operation = $this->resourceMetadataCollectionFactory?->create($context['resource_class'])->getOperation();
8181
if (
82-
null !== ($context['uri_variables'] ?? null)
83-
&& $operation instanceof HttpOperation
84-
&& \count($operation->getUriVariables() ?? []) > 1
82+
!$operation || (
83+
null !== ($context['uri_variables'] ?? null)
84+
&& $operation instanceof HttpOperation
85+
&& \count($operation->getUriVariables() ?? []) > 1
86+
)
8587
) {
8688
throw new InvalidArgumentException('Cannot find object to populate, use JSON-LD or specify an IRI at path "id".');
8789
}
8890
$uriVariables = $this->getContextUriVariables($data, $operation, $context);
8991
$iri = $this->iriConverter->getIriFromResource($context['resource_class'], UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => $uriVariables]);
9092

91-
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri($iri, ['fetch_data' => true]);
93+
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri($iri, $context + ['fetch_data' => true]);
9294
}
9395
}
9496

0 commit comments

Comments
 (0)