Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 15d075f

Browse files
committed
feature #46680 [Serializer] Provide context information from attribute for promoted properties (DanielBadura)
This PR was submitted for the 5.4 branch but it was squashed and merged into the 6.2 branch instead. Discussion ---------- [Serializer] Provide context information from attribute for promoted properties | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - I refactored some classes to using property promotion wiht `#[Context]` attributes and my tests started to fail since the context was missing then. So i tried to retrieve this context at the `AbstractNormalizer` and added a testcase for that. I'm not sure if this is the right place for getting this metadata information and if i have overseen something. Commits ------- c51c4e6 [Serializer] Provide context information from attribute for promoted properties
2 parents 46f7f27 + c51c4e6 commit 15d075f

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
334334
$params = [];
335335
foreach ($constructorParameters as $constructorParameter) {
336336
$paramName = $constructorParameter->name;
337-
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
337+
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
338+
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $attributeContext) : $paramName;
338339

339340
$allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes);
340341
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
@@ -346,7 +347,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
346347

347348
$variadicParameters = [];
348349
foreach ($data[$paramName] as $parameterData) {
349-
$variadicParameters[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
350+
$variadicParameters[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $attributeContext, $format);
350351
}
351352

352353
$params = array_merge($params, $variadicParameters);
@@ -363,7 +364,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
363364

364365
// Don't run set for a parameter passed to the constructor
365366
try {
366-
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
367+
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $attributeContext, $format);
367368
} catch (NotNormalizableValueException $exception) {
368369
if (!isset($context['not_normalizable_value_exceptions'])) {
369370
throw $exception;
@@ -485,4 +486,43 @@ final protected function applyCallbacks(mixed $value, object|string $object, str
485486

486487
return $callback ? $callback($value, $object, $attribute, $format, $context) : $value;
487488
}
489+
490+
/**
491+
* Computes the normalization context merged with current one. Metadata always wins over global context, as more specific.
492+
*
493+
* @internal
494+
*/
495+
protected function getAttributeNormalizationContext(object $object, string $attribute, array $context): array
496+
{
497+
if (null === $metadata = $this->getAttributeMetadata($object, $attribute)) {
498+
return $context;
499+
}
500+
501+
return array_merge($context, $metadata->getNormalizationContextForGroups($this->getGroups($context)));
502+
}
503+
504+
/**
505+
* Computes the denormalization context merged with current one. Metadata always wins over global context, as more specific.
506+
*
507+
* @internal
508+
*/
509+
protected function getAttributeDenormalizationContext(string $class, string $attribute, array $context): array
510+
{
511+
$context['deserialization_path'] = ($context['deserialization_path'] ?? false) ? $context['deserialization_path'].'.'.$attribute : $attribute;
512+
513+
if (null === $metadata = $this->getAttributeMetadata($class, $attribute)) {
514+
return $context;
515+
}
516+
517+
return array_merge($context, $metadata->getDenormalizationContextForGroups($this->getGroups($context)));
518+
}
519+
520+
private function getAttributeMetadata($objectOrClass, string $attribute): ?AttributeMetadataInterface
521+
{
522+
if (!$this->classMetadataFactory) {
523+
return null;
524+
}
525+
526+
return $this->classMetadataFactory->getMetadataFor($objectOrClass)->getAttributesMetadata()[$attribute] ?? null;
527+
}
488528
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[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+
namespace Symfony\Component\Serializer\Tests\Fixtures\Annotations;
13+
14+
use Symfony\Component\Serializer\Annotation\Context;
15+
16+
/**
17+
* @author Maxime Steinhausser <[email protected]>
18+
*/
19+
class ContextDummyPromotedProperties extends ContextDummyParent
20+
{
21+
public function __construct(
22+
/**
23+
* @Context({ "foo" = "value", "bar" = "value", "nested" = {
24+
* "nested_key" = "nested_value",
25+
* }, "array": { "first", "second" } })
26+
* @Context({ "bar" = "value_for_group_a" }, groups = "a")
27+
*/
28+
public $foo,
29+
30+
/**
31+
* @Context(
32+
* normalizationContext = { "format" = "d/m/Y" },
33+
* denormalizationContext = { "format" = "m-d-Y H:i" },
34+
* groups = {"a", "b"}
35+
* )
36+
*/
37+
public $bar,
38+
39+
/**
40+
* @Context(normalizationContext={ "prop" = "dummy_value" })
41+
*/
42+
public $overriddenParentProperty,
43+
) {
44+
}
45+
46+
/**
47+
* @Context({ "method" = "method_with_context" })
48+
*/
49+
public function getMethodWithContext()
50+
{
51+
return 'method_with_context';
52+
}
53+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[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+
namespace Symfony\Component\Serializer\Tests\Fixtures\Attributes;
13+
14+
use Symfony\Component\Serializer\Annotation\Context;
15+
16+
/**
17+
* @author Maxime Steinhausser <[email protected]>
18+
*/
19+
class ContextDummyPromotedProperties extends ContextDummyParent
20+
{
21+
public function __construct(
22+
#[Context(['foo' => 'value', 'bar' => 'value', 'nested' => [
23+
'nested_key' => 'nested_value',
24+
], 'array' => ['first', 'second']])]
25+
#[Context(context: ['bar' => 'value_for_group_a'], groups: ['a'])]
26+
public $foo,
27+
28+
#[Context(
29+
normalizationContext: ['format' => 'd/m/Y'],
30+
denormalizationContext: ['format' => 'm-d-Y H:i'],
31+
groups: ['a', 'b'],
32+
)]
33+
public $bar,
34+
35+
#[Context(normalizationContext: ['prop' => 'dummy_value'])]
36+
public $overriddenParentProperty,
37+
) {
38+
}
39+
40+
#[Context(['method' => 'method_with_context'])]
41+
public function getMethodWithContext()
42+
{
43+
return 'method_with_context';
44+
}
45+
}

src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ public function testLoadContexts()
120120
$this->assertLoadedContexts($this->getNamespace().'\ContextDummy', $this->getNamespace().'\ContextDummyParent');
121121
}
122122

123+
/**
124+
* @requires PHP 8
125+
*/
126+
public function testLoadContextsPropertiesPromoted()
127+
{
128+
$this->assertLoadedContexts($this->getNamespace().'\ContextDummyPromotedProperties', $this->getNamespace().'\ContextDummyParent');
129+
}
130+
123131
public function testThrowsOnContextOnInvalidMethod()
124132
{
125133
$class = $this->getNamespace().'\BadMethodContextDummy';

0 commit comments

Comments
 (0)