Skip to content

Commit d3484b0

Browse files
soyukaMariusJam
andauthored
fix(serializer): integrate root_resource_class to cache key (#6073)
* fix: operation cache key without operation name on related resources (de)normalization * test: cache key --------- Co-authored-by: Marius J <[email protected]>
1 parent 3685010 commit d3484b0

File tree

2 files changed

+83
-4
lines changed

2 files changed

+83
-4
lines changed

src/Serializer/AbstractItemNormalizer.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -580,9 +580,10 @@ protected function getFactoryOptions(array $context): array
580580
$options['serializer_groups'] = (array) $context[self::GROUPS];
581581
}
582582

583-
$operationCacheKey = ($context['resource_class'] ?? '').($context['operation_name'] ?? '').($context['api_normalize'] ?? '');
584-
if ($operationCacheKey && isset($this->localFactoryOptionsCache[$operationCacheKey])) {
585-
return $options + $this->localFactoryOptionsCache[$operationCacheKey];
583+
$operationCacheKey = ($context['resource_class'] ?? '').($context['operation_name'] ?? '').($context['root_operation_name'] ?? '');
584+
$suffix = ($context['api_normalize'] ?? '') ? 'n' : '';
585+
if ($operationCacheKey && isset($this->localFactoryOptionsCache[$operationCacheKey.$suffix])) {
586+
return $options + $this->localFactoryOptionsCache[$operationCacheKey.$suffix];
586587
}
587588

588589
// This is a hot spot
@@ -595,7 +596,7 @@ protected function getFactoryOptions(array $context): array
595596
}
596597
}
597598

598-
return $options + $this->localFactoryOptionsCache[$operationCacheKey] = $options;
599+
return $options + $this->localFactoryOptionsCache[$operationCacheKey.$suffix] = $options;
599600
}
600601

601602
/**

tests/Serializer/AbstractItemNormalizerTest.php

+78
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,84 @@ public function testDenormalizeObjectWithNullDisabledTypeEnforcement(): void
13991399
$this->assertInstanceOf(DtoWithNullValue::class, $actual);
14001400
$this->assertEquals(new DtoWithNullValue(), $actual);
14011401
}
1402+
1403+
public function testCacheKey(): void
1404+
{
1405+
$relatedDummy = new RelatedDummy();
1406+
1407+
$dummy = new Dummy();
1408+
$dummy->setName('foo');
1409+
$dummy->setAlias('ignored');
1410+
$dummy->setRelatedDummy($relatedDummy);
1411+
$dummy->relatedDummies->add(new RelatedDummy());
1412+
1413+
$relatedDummies = new ArrayCollection([$relatedDummy]);
1414+
1415+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
1416+
$propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['name', 'alias', 'relatedDummy', 'relatedDummies']));
1417+
1418+
$relatedDummyType = new Type(Type::BUILTIN_TYPE_OBJECT, false, RelatedDummy::class);
1419+
$relatedDummiesType = new Type(Type::BUILTIN_TYPE_OBJECT, false, ArrayCollection::class, true, new Type(Type::BUILTIN_TYPE_INT), $relatedDummyType);
1420+
1421+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
1422+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
1423+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withDescription('')->withReadable(true));
1424+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummyType])->withDescription('')->withReadable(true)->withWritable(false)->withReadableLink(false));
1425+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([$relatedDummiesType])->withReadable(true)->withWritable(false)->withReadableLink(false));
1426+
1427+
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
1428+
$iriConverterProphecy->getIriFromResource($dummy, Argument::cetera())->willReturn('/dummies/1');
1429+
$iriConverterProphecy->getIriFromResource($relatedDummy, Argument::cetera())->willReturn('/dummies/2');
1430+
1431+
$propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
1432+
$propertyAccessorProphecy->getValue($dummy, 'name')->willReturn('foo');
1433+
$propertyAccessorProphecy->getValue($dummy, 'relatedDummy')->willReturn($relatedDummy);
1434+
$propertyAccessorProphecy->getValue($dummy, 'relatedDummies')->willReturn($relatedDummies);
1435+
1436+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
1437+
$resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
1438+
$resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class);
1439+
$resourceClassResolverProphecy->getResourceClass($relatedDummy, RelatedDummy::class)->willReturn(RelatedDummy::class);
1440+
$resourceClassResolverProphecy->getResourceClass($relatedDummies, RelatedDummy::class)->willReturn(RelatedDummy::class);
1441+
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
1442+
$resourceClassResolverProphecy->isResourceClass(RelatedDummy::class)->willReturn(true);
1443+
1444+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
1445+
$serializerProphecy->willImplement(NormalizerInterface::class);
1446+
$serializerProphecy->normalize('foo', null, Argument::type('array'))->willReturn('foo');
1447+
$serializerProphecy->normalize(['/dummies/2'], null, Argument::type('array'))->willReturn(['/dummies/2']);
1448+
1449+
$normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [
1450+
$propertyNameCollectionFactoryProphecy->reveal(),
1451+
$propertyMetadataFactoryProphecy->reveal(),
1452+
$iriConverterProphecy->reveal(),
1453+
$resourceClassResolverProphecy->reveal(),
1454+
$propertyAccessorProphecy->reveal(),
1455+
null,
1456+
null,
1457+
[],
1458+
null,
1459+
null,
1460+
]);
1461+
$normalizer->setSerializer($serializerProphecy->reveal());
1462+
1463+
$expected = [
1464+
'name' => 'foo',
1465+
'relatedDummy' => '/dummies/2',
1466+
'relatedDummies' => ['/dummies/2'],
1467+
];
1468+
$this->assertSame($expected, $normalizer->normalize($dummy, null, [
1469+
'resources' => [],
1470+
'groups' => ['group'],
1471+
'ignored_attributes' => ['alias'],
1472+
'operation_name' => 'operation_name',
1473+
'root_operation_name' => 'root_operation_name',
1474+
]));
1475+
1476+
$operationCacheKey = (new \ReflectionClass($normalizer))->getProperty('localFactoryOptionsCache')->getValue($normalizer);
1477+
$this->assertEquals(array_keys($operationCacheKey), [sprintf('%s%s%s%s', Dummy::class, 'operation_name', 'root_operation_name', 'n')]);
1478+
$this->assertEquals(current($operationCacheKey), ['serializer_groups' => ['group']]);
1479+
}
14021480
}
14031481

14041482
class ObjectWithBasicProperties

0 commit comments

Comments
 (0)