|
16 | 16 | use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
|
17 | 17 | use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
|
18 | 18 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
19 |
| -use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; |
| 19 | +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; |
20 | 20 | use Symfony\Component\PropertyInfo\Type;
|
21 | 21 | use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
22 | 22 | use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
@@ -47,6 +47,7 @@ final class LiveComponentHydrator
|
47 | 47 | public function __construct(
|
48 | 48 | private iterable $hydrationExtensions,
|
49 | 49 | private PropertyAccessorInterface $propertyAccessor,
|
| 50 | + private PropertyTypeExtractorInterface $propertyTypeExtractor, |
50 | 51 | private NormalizerInterface|DenormalizerInterface $normalizer,
|
51 | 52 | private string $secret
|
52 | 53 | ) {
|
@@ -343,22 +344,15 @@ private function dehydrateValue(mixed $value, LivePropMetadata $propMetadata, ob
|
343 | 344 | }
|
344 | 345 |
|
345 | 346 | if (\is_array($value)) {
|
346 |
| - if ($propMetadata->collectionValueType() && Type::BUILTIN_TYPE_OBJECT === $propMetadata->collectionValueType()->getBuiltinType()) { |
347 |
| - $collectionClass = $propMetadata->collectionValueType()->getClassName(); |
348 |
| - foreach ($value as $key => $objectItem) { |
349 |
| - if (!$objectItem instanceof $collectionClass) { |
350 |
| - throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" is an array. We determined the array is full of %s objects, but at least on key had a different value of %s', $propMetadata->getName(), $component::class, $collectionClass, get_debug_type($objectItem))); |
351 |
| - } |
352 |
| - |
353 |
| - $value[$key] = $this->dehydrateObjectValue($objectItem, $collectionClass, $propMetadata->getFormat(), $component::class, sprintf('%s.%s', $propMetadata->getName(), $key)); |
| 347 | + foreach ($value as $key => $objectItem) { |
| 348 | + $type = \gettype($objectItem); |
| 349 | + if ('object' === $type) { |
| 350 | + $type = $objectItem::class; |
354 | 351 | }
|
355 |
| - } |
356 | 352 |
|
357 |
| - if (!$this->isValueValidDehydratedValue($value)) { |
358 |
| - $badKeys = $this->getNonScalarKeys($value, $propMetadata->getName()); |
359 |
| - $badKeysText = implode(', ', array_map(fn ($key) => sprintf('%s: %s', $key, $badKeys[$key]), array_keys($badKeys))); |
| 353 | + $propMetadata = new LivePropMetadata($key, new LiveProp(true), $type, false, true, null); |
360 | 354 |
|
361 |
| - throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" is an array, but it contains one or more keys that are not scalars: %s', $propMetadata->getName(), $component::class, $badKeysText)); |
| 355 | + $value[$key] = $this->dehydrateValue($objectItem, $propMetadata, $component); |
362 | 356 | }
|
363 | 357 |
|
364 | 358 | return $value;
|
@@ -394,19 +388,11 @@ private function dehydrateObjectValue(object $value, string $classType, ?string
|
394 | 388 | }
|
395 | 389 | }
|
396 | 390 |
|
397 |
| - $reflexionExtractor = new ReflectionExtractor(); |
398 |
| - $properties = $reflexionExtractor->getProperties($classType); |
399 | 391 | $propertiesValues = [];
|
400 |
| - foreach ($properties as $property) { |
401 |
| - if ($reflexionExtractor->isReadable($classType, $property)) { |
402 |
| - $propertyValue = $this->propertyAccessor->getValue($value, $property); |
403 |
| - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getBuiltinType(); |
404 |
| - if ($type === 'object') { |
405 |
| - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getClassName(); |
406 |
| - } |
407 |
| - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, false, true, null); |
408 |
| - $propertiesValues[$property] = $this->dehydrateValue($propertyValue, $propMetadata, $component); |
409 |
| - } |
| 392 | + foreach ((new \ReflectionClass($classType))->getProperties() as $property) { |
| 393 | + $propertyValue = $this->propertyAccessor->getValue($value, $property->getName()); |
| 394 | + $propMetadata = $this->generateLivePropMetadata($classType, $property->getName()); |
| 395 | + $propertiesValues[$property->getName()] = $this->dehydrateValue($propertyValue, $propMetadata, $component); |
410 | 396 | }
|
411 | 397 |
|
412 | 398 | return $propertiesValues;
|
@@ -484,17 +470,10 @@ private function hydrateObjectValue(mixed $value, string $className, bool $allow
|
484 | 470 | }
|
485 | 471 | }
|
486 | 472 |
|
487 |
| - if (is_array($value)) { |
488 |
| - $object = new $className; |
489 |
| - $extractor = new ReflectionExtractor(); |
| 473 | + if (\is_array($value)) { |
| 474 | + $object = new $className(); |
490 | 475 | foreach ($value as $property => $propertyValue) {
|
491 |
| - $type = $extractor->getTypes($className, $property)[0]->getBuiltinType(); |
492 |
| - $buildIn = true; |
493 |
| - if ($type === 'object') { |
494 |
| - $type = $extractor->getTypes($className, $property)[0]->getClassName(); |
495 |
| - $buildIn = false; |
496 |
| - } |
497 |
| - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, $buildIn, true, null); |
| 476 | + $propMetadata = $this->generateLivePropMetadata($className, $property); |
498 | 477 | $this->propertyAccessor->setValue($object, $property, $this->hydrateValue($propertyValue, $propMetadata, $component));
|
499 | 478 | }
|
500 | 479 |
|
@@ -593,4 +572,35 @@ private function recursiveKeySort(array &$data): void
|
593 | 572 | }
|
594 | 573 | ksort($data);
|
595 | 574 | }
|
| 575 | + |
| 576 | + private function generateLivePropMetadata(string $className, string $propertyName): LivePropMetadata |
| 577 | + { |
| 578 | + $reflexionClass = new \ReflectionClass($className); |
| 579 | + $property = $reflexionClass->getProperty($propertyName); |
| 580 | + |
| 581 | + $collectionValueType = null; |
| 582 | + $infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? []; |
| 583 | + foreach ($infoTypes as $infoType) { |
| 584 | + if ($infoType->isCollection()) { |
| 585 | + foreach ($infoType->getCollectionValueTypes() as $valueType) { |
| 586 | + $collectionValueType = $valueType; |
| 587 | + break; |
| 588 | + } |
| 589 | + } |
| 590 | + } |
| 591 | + |
| 592 | + $type = $property->getType(); |
| 593 | + if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { |
| 594 | + throw new \LogicException(sprintf('Union or intersection types are not supported for LiveProps. You may want to change the type of property %s in %s.', $property->getName(), $property->getDeclaringClass()->getName())); |
| 595 | + } |
| 596 | + |
| 597 | + return new LivePropMetadata( |
| 598 | + $property->getName(), |
| 599 | + new LiveProp(true), |
| 600 | + $type ? $type->getName() : null, |
| 601 | + $type ? $type->isBuiltin() : false, |
| 602 | + $type ? $type->allowsNull() : true, |
| 603 | + $collectionValueType, |
| 604 | + ); |
| 605 | + } |
596 | 606 | }
|
0 commit comments