@@ -138,8 +138,6 @@ public function __construct(
138
138
}
139
139
140
140
/**
141
- * @param array $context
142
- *
143
141
* @return bool
144
142
*/
145
143
public function supportsNormalization (mixed $ data , string $ format = null /* , array $context = [] */ )
@@ -206,6 +204,11 @@ public function normalize(mixed $object, string $format = null, array $context =
206
204
$ stack [$ attribute ] = $ this ->applyCallbacks ($ attributeValue , $ object , $ attribute , $ format , $ attributeContext );
207
205
}
208
206
207
+ $ objectVersion = $ this ->getObjectVersion ($ object , $ stack );
208
+ if (null !== $ objectVersion ) {
209
+ $ stack = $ this ->filterAttributesByVersion ($ objectVersion , $ attributesMetadata , $ stack );
210
+ }
211
+
209
212
foreach ($ stack as $ attribute => $ attributeValue ) {
210
213
$ attributeContext = $ this ->getAttributeNormalizationContext ($ object , $ attribute , $ context );
211
214
@@ -320,7 +323,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
320
323
}
321
324
322
325
$ allowedAttributes = $ this ->getAllowedAttributes ($ type , $ context , true );
326
+ $ versionedConstraintAttributeCallables = $ this ->getVersionedConstraintAttributeCallables ($ type );
323
327
$ normalizedData = $ this ->prepareForDenormalization ($ data );
328
+
324
329
$ extraAttributes = [];
325
330
326
331
$ mappedClass = $ this ->getMappedClass ($ normalizedData , $ type , $ context );
@@ -336,6 +341,7 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
336
341
$ normalizedData = $ this ->removeNestedValue ($ serializedPath ->getElements (), $ normalizedData );
337
342
}
338
343
344
+ $ objectVersion = $ this ->getObjectVersion ($ type , $ normalizedData );
339
345
$ normalizedData = array_merge ($ normalizedData , $ nestedData );
340
346
341
347
$ object = $ this ->instantiateObject ($ normalizedData , $ mappedClass , $ context , new \ReflectionClass ($ mappedClass ), $ allowedAttributes , $ format );
@@ -382,6 +388,9 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
382
388
}
383
389
384
390
$ value = $ this ->applyCallbacks ($ value , $ resolvedClass , $ attribute , $ format , $ attributeContext );
391
+ if (null !== $ objectVersion ) {
392
+ $ value = $ this ->getValueByVersion ($ value , $ objectVersion , $ attribute , $ versionedConstraintAttributeCallables );
393
+ }
385
394
386
395
try {
387
396
$ this ->setAttributeValue ($ object , $ attribute , $ value , $ format , $ attributeContext );
@@ -594,6 +603,21 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
594
603
throw NotNormalizableValueException::createForUnexpectedDataType (sprintf ('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given). ' , $ attribute , $ currentClass , implode ('", " ' , array_keys ($ expectedTypes )), get_debug_type ($ data )), $ data , array_keys ($ expectedTypes ), $ context ['deserialization_path ' ] ?? $ attribute );
595
604
}
596
605
606
+ private function getObjectVersion (object |string $ objectOrClass , array $ stack ): ?string
607
+ {
608
+ $ objectVersion = null ;
609
+ foreach ($ this ->classMetadataFactory ?->getMetadataFor($ objectOrClass )->getAttributesMetadata () ?? [] as $ attribute => $ attributeMetadata ) {
610
+ if ($ attributeMetadata ->isVersion ()) {
611
+ if (null !== $ objectVersion ) {
612
+ throw new LogicException (sprintf ('Too many version ["%s","%s] attributes class "%s". ' , $ attribute , $ objectVersion , \is_object ($ objectOrClass ) ? $ objectOrClass ::class : $ objectOrClass ));
613
+ }
614
+ $ objectVersion = $ attribute ;
615
+ }
616
+ }
617
+
618
+ return $ objectVersion ? $ stack [$ objectVersion ] : null ;
619
+ }
620
+
597
621
/**
598
622
* @internal
599
623
*/
@@ -812,4 +836,43 @@ private function getMappedClass(array $data, string $class, array $context): str
812
836
813
837
return $ mappedClass ;
814
838
}
839
+
840
+ /**
841
+ * @param array<string, AttributeMetadataInterface> $attributesMetadata
842
+ */
843
+ private function filterAttributesByVersion (string $ objectVersion , array $ attributesMetadata , array $ stack ): array
844
+ {
845
+ return array_filter (
846
+ $ stack ,
847
+ fn (string $ key ) => $ this ->isVersionCompatible ($ objectVersion , $ attributesMetadata , $ key ),
848
+ \ARRAY_FILTER_USE_KEY
849
+ );
850
+ }
851
+
852
+ /**
853
+ * @param array<string, callable> $callables
854
+ */
855
+ private function getValueByVersion (mixed $ value , string $ objectVersion , string $ attribute , array $ callables ): mixed
856
+ {
857
+ if (!isset ($ callables [$ attribute ])) {
858
+ return $ value ;
859
+ }
860
+
861
+ return $ callables [$ attribute ]($ objectVersion ) ? $ value : null ;
862
+ }
863
+
864
+ /**
865
+ * @param class-string $class
866
+ *
867
+ * @return array<class-string, Closure(string)>
868
+ */
869
+ private function getVersionedConstraintAttributeCallables (string $ class ): array
870
+ {
871
+ $ attributes = [];
872
+ foreach ($ this ->classMetadataFactory ?->getMetadataFor($ class )?->getAttributesMetadata() ?? [] as $ attribute => $ attributeMetadata ) {
873
+ $ attributes [$ attribute ] = $ attributeMetadata ->isVersionCompatible (...);
874
+ }
875
+
876
+ return $ attributes ;
877
+ }
815
878
}
0 commit comments