vendor/symfony/doctrine-bridge/PropertyInfo/DoctrineExtractor.php line 73

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bridge\Doctrine\PropertyInfo;
  11. use Doctrine\DBAL\Types\Type as DBALType;
  12. use Doctrine\DBAL\Types\Types;
  13. use Doctrine\ORM\EntityManagerInterface;
  14. use Doctrine\ORM\Mapping\ClassMetadata;
  15. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  16. use Doctrine\ORM\Mapping\MappingException as OrmMappingException;
  17. use Doctrine\Persistence\Mapping\MappingException;
  18. use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
  19. use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
  20. use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
  21. use Symfony\Component\PropertyInfo\Type;
  22. /**
  23.  * Extracts data using Doctrine ORM and ODM metadata.
  24.  *
  25.  * @author Kévin Dunglas <dunglas@gmail.com>
  26.  */
  27. class DoctrineExtractor implements PropertyListExtractorInterfacePropertyTypeExtractorInterfacePropertyAccessExtractorInterface
  28. {
  29.     private $entityManager;
  30.     private $classMetadataFactory;
  31.     private static $useDeprecatedConstants;
  32.     public function __construct(EntityManagerInterface $entityManager)
  33.     {
  34.         $this->entityManager $entityManager;
  35.         if (null === self::$useDeprecatedConstants) {
  36.             self::$useDeprecatedConstants = !class_exists(Types::class);
  37.         }
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      */
  42.     public function getProperties(string $class, array $context = [])
  43.     {
  44.         if (null === $metadata $this->getMetadata($class)) {
  45.             return null;
  46.         }
  47.         $properties array_merge($metadata->getFieldNames(), $metadata->getAssociationNames());
  48.         if ($metadata instanceof ClassMetadataInfo && class_exists('Doctrine\ORM\Mapping\Embedded') && $metadata->embeddedClasses) {
  49.             $properties array_filter($properties, function ($property) {
  50.                 return false === strpos($property'.');
  51.             });
  52.             $properties array_merge($propertiesarray_keys($metadata->embeddedClasses));
  53.         }
  54.         return $properties;
  55.     }
  56.     /**
  57.      * {@inheritdoc}
  58.      */
  59.     public function getTypes(string $classstring $property, array $context = [])
  60.     {
  61.         if (null === $metadata $this->getMetadata($class)) {
  62.             return null;
  63.         }
  64.         if ($metadata->hasAssociation($property)) {
  65.             $class $metadata->getAssociationTargetClass($property);
  66.             if ($metadata->isSingleValuedAssociation($property)) {
  67.                 if ($metadata instanceof ClassMetadataInfo) {
  68.                     $associationMapping $metadata->getAssociationMapping($property);
  69.                     $nullable $this->isAssociationNullable($associationMapping);
  70.                 } else {
  71.                     $nullable false;
  72.                 }
  73.                 return [new Type(Type::BUILTIN_TYPE_OBJECT$nullable$class)];
  74.             }
  75.             $collectionKeyType Type::BUILTIN_TYPE_INT;
  76.             if ($metadata instanceof ClassMetadataInfo) {
  77.                 $associationMapping $metadata->getAssociationMapping($property);
  78.                 if (isset($associationMapping['indexBy'])) {
  79.                     $indexProperty $associationMapping['indexBy'];
  80.                     /** @var ClassMetadataInfo $subMetadata */
  81.                     $subMetadata $this->entityManager $this->entityManager->getClassMetadata($associationMapping['targetEntity']) : $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']);
  82.                     $typeOfField $subMetadata->getTypeOfField($indexProperty);
  83.                     if (null === $typeOfField) {
  84.                         $associationMapping $subMetadata->getAssociationMapping($indexProperty);
  85.                         /** @var ClassMetadataInfo $subMetadata */
  86.                         $indexProperty $subMetadata->getSingleAssociationReferencedJoinColumnName($indexProperty);
  87.                         $subMetadata $this->entityManager $this->entityManager->getClassMetadata($associationMapping['targetEntity']) : $this->classMetadataFactory->getMetadataFor($associationMapping['targetEntity']);
  88.                         $typeOfField $subMetadata->getTypeOfField($indexProperty);
  89.                     }
  90.                     if (!$collectionKeyType $this->getPhpType($typeOfField)) {
  91.                         return null;
  92.                     }
  93.                 }
  94.             }
  95.             return [new Type(
  96.                 Type::BUILTIN_TYPE_OBJECT,
  97.                 false,
  98.                 'Doctrine\Common\Collections\Collection',
  99.                 true,
  100.                 new Type($collectionKeyType),
  101.                 new Type(Type::BUILTIN_TYPE_OBJECTfalse$class)
  102.             )];
  103.         }
  104.         if ($metadata instanceof ClassMetadataInfo && class_exists('Doctrine\ORM\Mapping\Embedded') && isset($metadata->embeddedClasses[$property])) {
  105.             return [new Type(Type::BUILTIN_TYPE_OBJECTfalse$metadata->embeddedClasses[$property]['class'])];
  106.         }
  107.         if ($metadata->hasField($property)) {
  108.             $typeOfField $metadata->getTypeOfField($property);
  109.             if (!$builtinType $this->getPhpType($typeOfField)) {
  110.                 return null;
  111.             }
  112.             $nullable $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property);
  113.             switch ($builtinType) {
  114.                 case Type::BUILTIN_TYPE_OBJECT:
  115.                     switch ($typeOfField) {
  116.                         case self::$useDeprecatedConstants DBALType::DATE Types::DATE_MUTABLE:
  117.                         case self::$useDeprecatedConstants DBALType::DATETIME Types::DATETIME_MUTABLE:
  118.                         case self::$useDeprecatedConstants DBALType::DATETIMETZ Types::DATETIMETZ_MUTABLE:
  119.                         case 'vardatetime':
  120.                         case self::$useDeprecatedConstants DBALType::TIME Types::TIME_MUTABLE:
  121.                             return [new Type(Type::BUILTIN_TYPE_OBJECT$nullable'DateTime')];
  122.                         case 'date_immutable':
  123.                         case 'datetime_immutable':
  124.                         case 'datetimetz_immutable':
  125.                         case 'time_immutable':
  126.                             return [new Type(Type::BUILTIN_TYPE_OBJECT$nullable'DateTimeImmutable')];
  127.                         case 'dateinterval':
  128.                             return [new Type(Type::BUILTIN_TYPE_OBJECT$nullable'DateInterval')];
  129.                     }
  130.                     break;
  131.                 case Type::BUILTIN_TYPE_ARRAY:
  132.                     switch ($typeOfField) {
  133.                         case self::$useDeprecatedConstants DBALType::TARRAY Types::ARRAY:
  134.                         case 'json_array':
  135.                             return [new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue)];
  136.                         case self::$useDeprecatedConstants DBALType::SIMPLE_ARRAY Types::SIMPLE_ARRAY:
  137.                             return [new Type(Type::BUILTIN_TYPE_ARRAY$nullablenulltrue, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))];
  138.                     }
  139.             }
  140.             return [new Type($builtinType$nullable)];
  141.         }
  142.         return null;
  143.     }
  144.     /**
  145.      * {@inheritdoc}
  146.      */
  147.     public function isReadable(string $classstring $property, array $context = [])
  148.     {
  149.         return null;
  150.     }
  151.     /**
  152.      * {@inheritdoc}
  153.      */
  154.     public function isWritable(string $classstring $property, array $context = [])
  155.     {
  156.         if (
  157.             null === ($metadata $this->getMetadata($class))
  158.             || ClassMetadata::GENERATOR_TYPE_NONE === $metadata->generatorType
  159.             || !\in_array($property$metadata->getIdentifierFieldNames(), true)
  160.         ) {
  161.             return null;
  162.         }
  163.         return false;
  164.     }
  165.     private function getMetadata(string $class): ?ClassMetadata
  166.     {
  167.         try {
  168.             return $this->entityManager $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class);
  169.         } catch (MappingException OrmMappingException $exception) {
  170.             return null;
  171.         }
  172.     }
  173.     /**
  174.      * Determines whether an association is nullable.
  175.      *
  176.      * @see https://github.com/doctrine/doctrine2/blob/v2.5.4/lib/Doctrine/ORM/Tools/EntityGenerator.php#L1221-L1246
  177.      */
  178.     private function isAssociationNullable(array $associationMapping): bool
  179.     {
  180.         if (isset($associationMapping['id']) && $associationMapping['id']) {
  181.             return false;
  182.         }
  183.         if (!isset($associationMapping['joinColumns'])) {
  184.             return true;
  185.         }
  186.         $joinColumns $associationMapping['joinColumns'];
  187.         foreach ($joinColumns as $joinColumn) {
  188.             if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
  189.                 return false;
  190.             }
  191.         }
  192.         return true;
  193.     }
  194.     /**
  195.      * Gets the corresponding built-in PHP type.
  196.      */
  197.     private function getPhpType(string $doctrineType): ?string
  198.     {
  199.         switch ($doctrineType) {
  200.             case self::$useDeprecatedConstants DBALType::SMALLINT Types::SMALLINT:
  201.             case self::$useDeprecatedConstants DBALType::INTEGER Types::INTEGER:
  202.                 return Type::BUILTIN_TYPE_INT;
  203.             case self::$useDeprecatedConstants DBALType::FLOAT Types::FLOAT:
  204.                 return Type::BUILTIN_TYPE_FLOAT;
  205.             case self::$useDeprecatedConstants DBALType::BIGINT Types::BIGINT:
  206.             case self::$useDeprecatedConstants DBALType::STRING Types::STRING:
  207.             case self::$useDeprecatedConstants DBALType::TEXT Types::TEXT:
  208.             case self::$useDeprecatedConstants DBALType::GUID Types::GUID:
  209.             case self::$useDeprecatedConstants DBALType::DECIMAL Types::DECIMAL:
  210.                 return Type::BUILTIN_TYPE_STRING;
  211.             case self::$useDeprecatedConstants DBALType::BOOLEAN Types::BOOLEAN:
  212.                 return Type::BUILTIN_TYPE_BOOL;
  213.             case self::$useDeprecatedConstants DBALType::BLOB Types::BLOB:
  214.             case 'binary':
  215.                 return Type::BUILTIN_TYPE_RESOURCE;
  216.             case self::$useDeprecatedConstants DBALType::OBJECT Types::OBJECT:
  217.             case self::$useDeprecatedConstants DBALType::DATE Types::DATE_MUTABLE:
  218.             case self::$useDeprecatedConstants DBALType::DATETIME Types::DATETIME_MUTABLE:
  219.             case self::$useDeprecatedConstants DBALType::DATETIMETZ Types::DATETIMETZ_MUTABLE:
  220.             case 'vardatetime':
  221.             case self::$useDeprecatedConstants DBALType::TIME Types::TIME_MUTABLE:
  222.             case 'date_immutable':
  223.             case 'datetime_immutable':
  224.             case 'datetimetz_immutable':
  225.             case 'time_immutable':
  226.             case 'dateinterval':
  227.                 return Type::BUILTIN_TYPE_OBJECT;
  228.             case self::$useDeprecatedConstants DBALType::TARRAY Types::ARRAY:
  229.             case self::$useDeprecatedConstants DBALType::SIMPLE_ARRAY Types::SIMPLE_ARRAY:
  230.             case 'json_array':
  231.                 return Type::BUILTIN_TYPE_ARRAY;
  232.         }
  233.         return null;
  234.     }
  235. }