1<?php 2 3declare(strict_types=1); 4 5namespace JMS\Serializer\Metadata\Driver; 6 7use Doctrine\Common\Persistence\ManagerRegistry; 8use Doctrine\Common\Persistence\Mapping\ClassMetadata as DoctrineClassMetadata; 9use JMS\Serializer\Metadata\ClassMetadata; 10use JMS\Serializer\Metadata\ExpressionPropertyMetadata; 11use JMS\Serializer\Metadata\PropertyMetadata; 12use JMS\Serializer\Metadata\StaticPropertyMetadata; 13use JMS\Serializer\Metadata\VirtualPropertyMetadata; 14use JMS\Serializer\Type\Parser; 15use JMS\Serializer\Type\ParserInterface; 16use Metadata\ClassMetadata as BaseClassMetadata; 17use Metadata\Driver\DriverInterface; 18 19/** 20 * This class decorates any other driver. If the inner driver does not provide a 21 * a property type, the decorator will guess based on Doctrine 2 metadata. 22 */ 23abstract class AbstractDoctrineTypeDriver implements DriverInterface 24{ 25 /** 26 * Map of doctrine 2 field types to JMS\Serializer types 27 * 28 * @var array 29 */ 30 protected $fieldMapping = [ 31 'string' => 'string', 32 'text' => 'string', 33 'blob' => 'string', 34 'guid' => 'string', 35 36 'integer' => 'integer', 37 'smallint' => 'integer', 38 'bigint' => 'integer', 39 40 'datetime' => 'DateTime', 41 'datetimetz' => 'DateTime', 42 'time' => 'DateTime', 43 'date' => 'DateTime', 44 45 'float' => 'float', 46 'decimal' => 'float', 47 48 'boolean' => 'boolean', 49 50 'array' => 'array', 51 'json_array' => 'array', 52 'simple_array' => 'array<string>', 53 ]; 54 /** 55 * @var DriverInterface 56 */ 57 protected $delegate; 58 /** 59 * @var ManagerRegistry 60 */ 61 protected $registry; 62 63 /** 64 * @var ParserInterface 65 */ 66 protected $typeParser; 67 68 public function __construct(DriverInterface $delegate, ManagerRegistry $registry, ?ParserInterface $typeParser = null) 69 { 70 $this->delegate = $delegate; 71 $this->registry = $registry; 72 $this->typeParser = $typeParser ?: new Parser(); 73 } 74 75 public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadata 76 { 77 /** @var ClassMetadata $classMetadata */ 78 $classMetadata = $this->delegate->loadMetadataForClass($class); 79 80 // Abort if the given class is not a mapped entity 81 if (!$doctrineMetadata = $this->tryLoadingDoctrineMetadata($class->name)) { 82 return $classMetadata; 83 } 84 85 $this->setDiscriminator($doctrineMetadata, $classMetadata); 86 87 // We base our scan on the internal driver's property list so that we 88 // respect any internal white/blacklisting like in the AnnotationDriver 89 foreach ($classMetadata->propertyMetadata as $key => $propertyMetadata) { 90 /** @var $propertyMetadata PropertyMetadata */ 91 92 // If the inner driver provides a type, don't guess anymore. 93 if ($propertyMetadata->type || $this->isVirtualProperty($propertyMetadata)) { 94 continue; 95 } 96 97 if ($this->hideProperty($doctrineMetadata, $propertyMetadata)) { 98 unset($classMetadata->propertyMetadata[$key]); 99 } 100 101 $this->setPropertyType($doctrineMetadata, $propertyMetadata); 102 } 103 104 return $classMetadata; 105 } 106 107 private function isVirtualProperty(PropertyMetadata $propertyMetadata): bool 108 { 109 return $propertyMetadata instanceof VirtualPropertyMetadata 110 || $propertyMetadata instanceof StaticPropertyMetadata 111 || $propertyMetadata instanceof ExpressionPropertyMetadata; 112 } 113 114 protected function setDiscriminator(DoctrineClassMetadata $doctrineMetadata, ClassMetadata $classMetadata): void 115 { 116 } 117 118 protected function hideProperty(DoctrineClassMetadata $doctrineMetadata, PropertyMetadata $propertyMetadata): bool 119 { 120 return false; 121 } 122 123 protected function setPropertyType(DoctrineClassMetadata $doctrineMetadata, PropertyMetadata $propertyMetadata): void 124 { 125 } 126 127 protected function tryLoadingDoctrineMetadata(string $className): ?DoctrineClassMetadata 128 { 129 if (!$manager = $this->registry->getManagerForClass($className)) { 130 return null; 131 } 132 133 if ($manager->getMetadataFactory()->isTransient($className)) { 134 return null; 135 } 136 137 return $manager->getClassMetadata($className); 138 } 139 140 protected function normalizeFieldType(string $type): ?string 141 { 142 if (!isset($this->fieldMapping[$type])) { 143 return null; 144 } 145 146 return $this->fieldMapping[$type]; 147 } 148} 149