vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php line 173

  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ODM\MongoDB\Mapping;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\ODM\MongoDB\Configuration;
  6. use Doctrine\ODM\MongoDB\ConfigurationException;
  7. use Doctrine\ODM\MongoDB\DocumentManager;
  8. use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
  9. use Doctrine\ODM\MongoDB\Event\OnClassMetadataNotFoundEventArgs;
  10. use Doctrine\ODM\MongoDB\Events;
  11. use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
  12. use Doctrine\ODM\MongoDB\Id\AutoGenerator;
  13. use Doctrine\ODM\MongoDB\Id\IdGenerator;
  14. use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
  15. use Doctrine\ODM\MongoDB\Id\UuidGenerator;
  16. use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
  17. use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
  18. use Doctrine\Persistence\Mapping\Driver\MappingDriver;
  19. use Doctrine\Persistence\Mapping\ReflectionService;
  20. use ReflectionException;
  21. use function assert;
  22. use function get_class;
  23. use function get_class_methods;
  24. use function in_array;
  25. use function interface_exists;
  26. use function trigger_deprecation;
  27. use function ucfirst;
  28. /**
  29.  * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
  30.  * metadata mapping informations of a class which describes how a class should be mapped
  31.  * to a document database.
  32.  *
  33.  * @internal
  34.  *
  35.  * @template-extends AbstractClassMetadataFactory<ClassMetadata>
  36.  *
  37.  * @method list<ClassMetadata> getAllMetadata()
  38.  * @method ClassMetadata[] getLoadedMetadata()
  39.  * @method ClassMetadata getMetadataFor($className)
  40.  */
  41. final class ClassMetadataFactory extends AbstractClassMetadataFactory
  42. {
  43.     /** @var string */
  44.     protected $cacheSalt '$MONGODBODMCLASSMETADATA';
  45.     /** @var DocumentManager The DocumentManager instance */
  46.     private DocumentManager $dm;
  47.     /** @var Configuration The Configuration instance */
  48.     private Configuration $config;
  49.     /** @var MappingDriver The used metadata driver. */
  50.     private MappingDriver $driver;
  51.     /** @var EventManager The event manager instance */
  52.     private EventManager $evm;
  53.     public function setDocumentManager(DocumentManager $dm): void
  54.     {
  55.         $this->dm $dm;
  56.     }
  57.     public function setConfiguration(Configuration $config): void
  58.     {
  59.         $this->config $config;
  60.     }
  61.     /**
  62.      * Lazy initialization of this stuff, especially the metadata driver,
  63.      * since these are not needed at all when a metadata cache is active.
  64.      */
  65.     protected function initialize(): void
  66.     {
  67.         $driver $this->config->getMetadataDriverImpl();
  68.         if ($driver === null) {
  69.             throw ConfigurationException::noMetadataDriverConfigured();
  70.         }
  71.         $this->driver      $driver;
  72.         $this->evm         $this->dm->getEventManager();
  73.         $this->initialized true;
  74.     }
  75.     /** @param string $className */
  76.     protected function onNotFoundMetadata($className)
  77.     {
  78.         if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
  79.             return null;
  80.         }
  81.         $eventArgs = new OnClassMetadataNotFoundEventArgs($className$this->dm);
  82.         $this->evm->dispatchEvent(Events::onClassMetadataNotFound$eventArgs);
  83.         return $eventArgs->getFoundMetadata();
  84.     }
  85.     /**
  86.      * @deprecated
  87.      *
  88.      * @param string $namespaceAlias
  89.      * @param string $simpleClassName
  90.      */
  91.     protected function getFqcnFromAlias($namespaceAlias$simpleClassName): string
  92.     {
  93.         return $this->config->getDocumentNamespace($namespaceAlias) . '\\' $simpleClassName;
  94.     }
  95.     protected function getDriver(): MappingDriver
  96.     {
  97.         return $this->driver;
  98.     }
  99.     protected function wakeupReflection(ClassMetadataInterface $classReflectionService $reflService): void
  100.     {
  101.     }
  102.     protected function initializeReflection(ClassMetadataInterface $classReflectionService $reflService): void
  103.     {
  104.     }
  105.     protected function isEntity(ClassMetadataInterface $class): bool
  106.     {
  107.         assert($class instanceof ClassMetadata);
  108.         return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument && ! $class->isView();
  109.     }
  110.     /** @param bool $rootEntityFound */
  111.     protected function doLoadMetadata($class$parent$rootEntityFound, array $nonSuperclassParents = []): void
  112.     {
  113.         assert($class instanceof ClassMetadata);
  114.         if ($parent instanceof ClassMetadata) {
  115.             $class->setInheritanceType($parent->inheritanceType);
  116.             $class->setDiscriminatorField($parent->discriminatorField);
  117.             $class->setDiscriminatorMap($parent->discriminatorMap);
  118.             $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
  119.             $class->setIdGeneratorType($parent->generatorType);
  120.             $this->addInheritedFields($class$parent);
  121.             $this->addInheritedRelations($class$parent);
  122.             $this->addInheritedIndexes($class$parent);
  123.             $this->setInheritedShardKey($class$parent);
  124.             $class->setIdentifier($parent->identifier);
  125.             $class->setVersioned($parent->isVersioned);
  126.             $class->setVersionField($parent->versionField);
  127.             $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
  128.             $class->setAlsoLoadMethods($parent->alsoLoadMethods);
  129.             $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
  130.             $class->setReadPreference($parent->readPreference$parent->readPreferenceTags);
  131.             $class->setWriteConcern($parent->writeConcern);
  132.             if ($parent->isMappedSuperclass) {
  133.                 $class->setCustomRepositoryClass($parent->customRepositoryClassName);
  134.             }
  135.             if ($parent->isFile) {
  136.                 $class->isFile true;
  137.                 $class->setBucketName($parent->bucketName);
  138.                 if ($parent->chunkSizeBytes !== null) {
  139.                     $class->setChunkSizeBytes($parent->chunkSizeBytes);
  140.                 }
  141.             }
  142.         }
  143.         // Invoke driver
  144.         try {
  145.             $this->driver->loadMetadataForClass($class->getName(), $class);
  146.         } catch (ReflectionException $e) {
  147.             throw MappingException::reflectionFailure($class->getName(), $e);
  148.         }
  149.         $this->validateIdentifier($class);
  150.         if ($parent instanceof ClassMetadata && $rootEntityFound && $parent->generatorType === $class->generatorType) {
  151.             if ($parent->generatorType) {
  152.                 $class->setIdGeneratorType($parent->generatorType);
  153.             }
  154.             if ($parent->generatorOptions) {
  155.                 $class->setIdGeneratorOptions($parent->generatorOptions);
  156.             }
  157.             if ($parent->idGenerator) {
  158.                 $class->setIdGenerator($parent->idGenerator);
  159.             }
  160.         } else {
  161.             $this->completeIdGeneratorMapping($class);
  162.         }
  163.         if ($parent instanceof ClassMetadata && $parent->isInheritanceTypeSingleCollection()) {
  164.             $class->setDatabase($parent->getDatabase());
  165.             $class->setCollection($parent->getCollection());
  166.         }
  167.         $class->setParentClasses($nonSuperclassParents);
  168.         $this->evm->dispatchEvent(
  169.             Events::loadClassMetadata,
  170.             new LoadClassMetadataEventArgs($class$this->dm),
  171.         );
  172.         // phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
  173.         if ($class->isChangeTrackingNotify()) {
  174.             trigger_deprecation(
  175.                 'doctrine/mongodb-odm',
  176.                 '2.4',
  177.                 'NOTIFY tracking policy used in class "%s" is deprecated. Please use DEFERRED_EXPLICIT instead.',
  178.                 $class->name,
  179.             );
  180.         }
  181.     }
  182.     /**
  183.      * Validates the identifier mapping.
  184.      *
  185.      * @throws MappingException
  186.      */
  187.     protected function validateIdentifier(ClassMetadata $class): void
  188.     {
  189.         if (! $class->identifier && $this->isEntity($class)) {
  190.             throw MappingException::identifierRequired($class->name);
  191.         }
  192.     }
  193.     protected function newClassMetadataInstance($className): ClassMetadata
  194.     {
  195.         return new ClassMetadata($className);
  196.     }
  197.     private function completeIdGeneratorMapping(ClassMetadata $class): void
  198.     {
  199.         $idGenOptions $class->generatorOptions;
  200.         switch ($class->generatorType) {
  201.             case ClassMetadata::GENERATOR_TYPE_AUTO:
  202.                 $class->setIdGenerator(new AutoGenerator());
  203.                 break;
  204.             case ClassMetadata::GENERATOR_TYPE_INCREMENT:
  205.                 $incrementGenerator = new IncrementGenerator();
  206.                 if (isset($idGenOptions['key'])) {
  207.                     $incrementGenerator->setKey((string) $idGenOptions['key']);
  208.                 }
  209.                 if (isset($idGenOptions['collection'])) {
  210.                     $incrementGenerator->setCollection((string) $idGenOptions['collection']);
  211.                 }
  212.                 if (isset($idGenOptions['startingId'])) {
  213.                     $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
  214.                 }
  215.                 $class->setIdGenerator($incrementGenerator);
  216.                 break;
  217.             case ClassMetadata::GENERATOR_TYPE_UUID:
  218.                 $uuidGenerator = new UuidGenerator();
  219.                 if (isset($idGenOptions['salt'])) {
  220.                     $uuidGenerator->setSalt((string) $idGenOptions['salt']);
  221.                 }
  222.                 $class->setIdGenerator($uuidGenerator);
  223.                 break;
  224.             case ClassMetadata::GENERATOR_TYPE_ALNUM:
  225.                 $alnumGenerator = new AlnumGenerator();
  226.                 if (isset($idGenOptions['pad'])) {
  227.                     $alnumGenerator->setPad((int) $idGenOptions['pad']);
  228.                 }
  229.                 if (isset($idGenOptions['chars'])) {
  230.                     $alnumGenerator->setChars((string) $idGenOptions['chars']);
  231.                 } elseif (isset($idGenOptions['awkwardSafe'])) {
  232.                     $alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']);
  233.                 }
  234.                 $class->setIdGenerator($alnumGenerator);
  235.                 break;
  236.             case ClassMetadata::GENERATOR_TYPE_CUSTOM:
  237.                 if (empty($idGenOptions['class'])) {
  238.                     throw MappingException::missingIdGeneratorClass($class->name);
  239.                 }
  240.                 $customGenerator = new $idGenOptions['class']();
  241.                 unset($idGenOptions['class']);
  242.                 if (! $customGenerator instanceof IdGenerator) {
  243.                     throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
  244.                 }
  245.                 $methods get_class_methods($customGenerator);
  246.                 foreach ($idGenOptions as $name => $value) {
  247.                     $method 'set' ucfirst($name);
  248.                     if (! in_array($method$methods)) {
  249.                         throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
  250.                     }
  251.                     $customGenerator->$method($value);
  252.                 }
  253.                 $class->setIdGenerator($customGenerator);
  254.                 break;
  255.             case ClassMetadata::GENERATOR_TYPE_NONE:
  256.                 break;
  257.             default:
  258.                 throw new MappingException('Unknown generator type: ' $class->generatorType);
  259.         }
  260.     }
  261.     /**
  262.      * Adds inherited fields to the subclass mapping.
  263.      */
  264.     private function addInheritedFields(ClassMetadata $subClassClassMetadata $parentClass): void
  265.     {
  266.         foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
  267.             if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
  268.                 $mapping['inherited'] = $parentClass->name;
  269.             }
  270.             if (! isset($mapping['declared'])) {
  271.                 $mapping['declared'] = $parentClass->name;
  272.             }
  273.             $subClass->addInheritedFieldMapping($mapping);
  274.         }
  275.         foreach ($parentClass->reflFields as $name => $field) {
  276.             $subClass->reflFields[$name] = $field;
  277.         }
  278.     }
  279.     /**
  280.      * Adds inherited association mappings to the subclass mapping.
  281.      *
  282.      * @throws MappingException
  283.      */
  284.     private function addInheritedRelations(ClassMetadata $subClassClassMetadata $parentClass): void
  285.     {
  286.         foreach ($parentClass->associationMappings as $field => $mapping) {
  287.             if ($parentClass->isMappedSuperclass) {
  288.                 $mapping['sourceDocument'] = $subClass->name;
  289.             }
  290.             if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
  291.                 $mapping['inherited'] = $parentClass->name;
  292.             }
  293.             if (! isset($mapping['declared'])) {
  294.                 $mapping['declared'] = $parentClass->name;
  295.             }
  296.             $subClass->addInheritedAssociationMapping($mapping);
  297.         }
  298.     }
  299.     /**
  300.      * Adds inherited indexes to the subclass mapping.
  301.      */
  302.     private function addInheritedIndexes(ClassMetadata $subClassClassMetadata $parentClass): void
  303.     {
  304.         foreach ($parentClass->indexes as $index) {
  305.             $subClass->addIndex($index['keys'], $index['options']);
  306.         }
  307.     }
  308.     /**
  309.      * Adds inherited shard key to the subclass mapping.
  310.      */
  311.     private function setInheritedShardKey(ClassMetadata $subClassClassMetadata $parentClass): void
  312.     {
  313.         if (! $parentClass->isSharded()) {
  314.             return;
  315.         }
  316.         $subClass->setShardKey(
  317.             $parentClass->shardKey['keys'],
  318.             $parentClass->shardKey['options'],
  319.         );
  320.     }
  321. }
  322. interface_exists(ClassMetadataInterface::class);
  323. interface_exists(ReflectionService::class);