Skip to content

Commit 40fcfac

Browse files
committed
fix(jsonld): genId false should work with embeded resources
1 parent 64ff50f commit 40fcfac

File tree

6 files changed

+89
-15
lines changed

6 files changed

+89
-15
lines changed

src/JsonLd/Serializer/ItemNormalizer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public function normalize(mixed $object, ?string $format = null, array $context
111111
} elseif ($this->contextBuilder instanceof AnonymousContextBuilderInterface) {
112112
if ($context['api_collection_sub_level'] ?? false) {
113113
unset($context['api_collection_sub_level']);
114-
$context['output']['genid'] = true;
114+
$context['output']['gen_id'] ??= true;
115115
$context['output']['iri'] = null;
116116
}
117117

@@ -124,7 +124,7 @@ public function normalize(mixed $object, ?string $format = null, array $context
124124
unset($context['operation'], $context['operation_name']);
125125
}
126126

127-
if (true === ($context['force_iri_generation'] ?? true) && $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context)) {
127+
if (true === ($context['output']['gen_id'] ?? true) && true === ($context['force_iri_generation'] ?? true) && $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context)) {
128128
$context['iri'] = $iri;
129129
$metadata['@id'] = $iri;
130130
}

src/Serializer/AbstractItemNormalizer.php

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ protected function getAttributeValue(object $object, string $attribute, ?string
670670
&& ($className = $collectionValueType->getClassName())
671671
&& $this->resourceClassResolver->isResourceClass($className)
672672
) {
673-
$childContext = $this->createChildContext($this->createOperationContext($context, $className), $attribute, $format);
673+
$childContext = $this->createChildContext($this->createOperationContext($context, $className, $propertyMetadata), $attribute, $format);
674674

675675
// @see ApiPlatform\Hal\Serializer\ItemNormalizer:getComponents logic for intentional duplicate content
676676
// @see ApiPlatform\JsonApi\Serializer\ItemNormalizer:getComponents logic for intentional duplicate content
@@ -707,7 +707,7 @@ protected function getAttributeValue(object $object, string $attribute, ?string
707707
($className = $type->getClassName())
708708
&& $this->resourceClassResolver->isResourceClass($className)
709709
) {
710-
$childContext = $this->createChildContext($this->createOperationContext($context, $className), $attribute, $format);
710+
$childContext = $this->createChildContext($this->createOperationContext($context, $className, $propertyMetadata), $attribute, $format);
711711
unset($childContext['iri'], $childContext['uri_variables'], $childContext['item_uri_template']);
712712
if ('jsonld' === $format && $uriTemplate = $propertyMetadata->getUriTemplate()) {
713713
$operation = $this->resourceMetadataCollectionFactory->create($className)->getOperation(
@@ -749,22 +749,18 @@ protected function getAttributeValue(object $object, string $attribute, ?string
749749

750750
// Anonymous resources
751751
if ($className) {
752-
$childContext = $this->createChildContext($this->createOperationContext($context, $className), $attribute, $format);
753-
$childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? true;
754-
752+
$childContext = $this->createChildContext($this->createOperationContext($context, $className, $propertyMetadata), $attribute, $format);
755753
$attributeValue = $this->propertyAccessor->getValue($object, $attribute);
756754

757755
return $this->serializer->normalize($attributeValue, $format, $childContext);
758756
}
759757

760758
if ('array' === $type->getBuiltinType()) {
761759
if ($className = ($type->getCollectionValueTypes()[0] ?? null)?->getClassName()) {
762-
$context = $this->createOperationContext($context, $className);
760+
$context = $this->createOperationContext($context, $className, $propertyMetadata);
763761
}
764762

765763
$childContext = $this->createChildContext($context, $attribute, $format);
766-
$childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? true;
767-
768764
$attributeValue = $this->propertyAccessor->getValue($object, $attribute);
769765

770766
return $this->serializer->normalize($attributeValue, $format, $childContext);
@@ -820,12 +816,12 @@ protected function normalizeCollectionOfRelations(ApiProperty $propertyMetadata,
820816
*/
821817
protected function normalizeRelation(ApiProperty $propertyMetadata, ?object $relatedObject, string $resourceClass, ?string $format, array $context): \ArrayObject|array|string|null
822818
{
823-
if (null === $relatedObject || !empty($context['attributes']) || $propertyMetadata->isReadableLink()) {
819+
if (null === $relatedObject || !empty($context['attributes']) || $propertyMetadata->isReadableLink() || false === ($context['output']['gen_id'] ?? true)) {
824820
if (!$this->serializer instanceof NormalizerInterface) {
825821
throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class));
826822
}
827823

828-
$relatedContext = $this->createOperationContext($context, $resourceClass);
824+
$relatedContext = $this->createOperationContext($context, $resourceClass, $propertyMetadata);
829825
$normalizedRelatedObject = $this->serializer->normalize($relatedObject, $format, $relatedContext);
830826
if (!\is_string($normalizedRelatedObject) && !\is_array($normalizedRelatedObject) && !$normalizedRelatedObject instanceof \ArrayObject && null !== $normalizedRelatedObject) {
831827
throw new UnexpectedValueException('Expected normalized relation to be an IRI, array, \ArrayObject or null');
@@ -917,7 +913,7 @@ private function createAndValidateAttributeValue(string $attribute, mixed $value
917913
&& $this->resourceClassResolver->isResourceClass($className)
918914
) {
919915
$resourceClass = $this->resourceClassResolver->getResourceClass(null, $className);
920-
$childContext = $this->createChildContext($this->createOperationContext($context, $resourceClass), $attribute, $format);
916+
$childContext = $this->createChildContext($this->createOperationContext($context, $resourceClass, $propertyMetadata), $attribute, $format);
921917

922918
try {
923919
return $this->denormalizeRelation($attribute, $propertyMetadata, $resourceClass, $value, $format, $childContext);

src/Serializer/OperationContextTrait.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
namespace ApiPlatform\Serializer;
1515

16+
use ApiPlatform\Metadata\ApiProperty;
17+
1618
/**
1719
* @internal
1820
*/
@@ -22,7 +24,7 @@ trait OperationContextTrait
2224
* This context is created when working on a relation context or items of a collection. It cleans the previously given
2325
* context as the operation changes.
2426
*/
25-
protected function createOperationContext(array $context, ?string $resourceClass = null): array
27+
protected function createOperationContext(array $context, ?string $resourceClass = null, ?ApiProperty $propertyMetadata = null): array
2628
{
2729
if (isset($context['operation']) && !isset($context['root_operation'])) {
2830
$context['root_operation'] = $context['operation'];
@@ -34,6 +36,12 @@ protected function createOperationContext(array $context, ?string $resourceClass
3436

3537
unset($context['iri'], $context['uri_variables'], $context['item_uri_template'], $context['force_resource_class']);
3638

39+
// At some point we should merge the jsonld context here, there's a TODO to simplify this somewhere else
40+
if ($propertyMetadata) {
41+
$context['output'] ??= [];
42+
$context['output']['gen_id'] = $propertyMetadata->getGenId() ?? true;
43+
}
44+
3745
if (!$resourceClass) {
3846
return $context;
3947
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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\GenIdFalse;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
19+
#[ApiResource(operations: [])]
20+
class AggregateRating
21+
{
22+
public function __construct(
23+
#[ApiProperty(iris: ['https://schema.org/ratingValue'])] public float $ratingValue,
24+
#[ApiProperty(iris: ['https://schema.org/ratingCount'])] public int $ratingCount,
25+
) {
26+
}
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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\GenIdFalse;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\Operation;
19+
20+
#[Get('/gen_id_falsy', provider: [self::class, 'getData'], normalizationContext: ['hydra_prefix' => false])]
21+
class GenIdFalse
22+
{
23+
public function __construct(public string $id, #[ApiProperty(genId: false)] public AggregateRating $aggregateRating)
24+
{
25+
}
26+
27+
public static function getData(Operation $operation, array $uriVariables = [], array $context = []): self
28+
{
29+
return new self('1', new AggregateRating(2, 3));
30+
}
31+
}

tests/Functional/JsonLdTest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace ApiPlatform\Tests\Functional;
1515

1616
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\AggregateRating;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\GenIdFalse;
1719
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810\JsonLdContextOutput;
1820
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Bar;
1921
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Foo;
@@ -32,7 +34,7 @@ class JsonLdTest extends ApiTestCase
3234
*/
3335
public static function getResources(): array
3436
{
35-
return [Foo::class, Bar::class, JsonLdContextOutput::class];
37+
return [Foo::class, Bar::class, JsonLdContextOutput::class, GenIdFalse::class, AggregateRating::class];
3638
}
3739

3840
/**
@@ -67,6 +69,16 @@ public function testContextWithOutput(): void
6769
]);
6870
}
6971

72+
public function testGenIdFalseOnResource(): void
73+
{
74+
$response = self::createClient()->request(
75+
'GET',
76+
'/gen_id_falsy',
77+
);
78+
$res = $response->toArray();
79+
dd($res);
80+
}
81+
7082
protected function setUp(): void
7183
{
7284
self::bootKernel();

0 commit comments

Comments
 (0)