vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php line 76

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ODM\MongoDB\Mapping\Driver;
  4. use Doctrine\Common\Annotations\AnnotationReader;
  5. use Doctrine\Common\Annotations\Reader;
  6. use Doctrine\ODM\MongoDB\Events;
  7. use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
  8. use Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractIndex;
  9. use Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey;
  10. use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
  11. use Doctrine\ODM\MongoDB\Mapping\MappingException;
  12. use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver;
  13. use MongoDB\Driver\Exception\UnexpectedValueException;
  14. use ReflectionClass;
  15. use ReflectionMethod;
  16. use function array_merge;
  17. use function array_replace;
  18. use function assert;
  19. use function class_exists;
  20. use function constant;
  21. use function count;
  22. use function get_class;
  23. use function interface_exists;
  24. use function is_array;
  25. use function MongoDB\BSON\fromJSON;
  26. use function MongoDB\BSON\toPHP;
  27. use function trigger_deprecation;
  28. /**
  29.  * The AnnotationDriver reads the mapping metadata from docblock annotations.
  30.  */
  31. class AnnotationDriver extends CompatibilityAnnotationDriver
  32. {
  33.     use ColocatedMappingDriver;
  34.     /**
  35.      * The annotation reader.
  36.      *
  37.      * @internal this property will be private in 3.0
  38.      *
  39.      * @var Reader
  40.      */
  41.     protected $reader;
  42.     /**
  43.      * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
  44.      * docblock annotations.
  45.      *
  46.      * @param Reader               $reader The AnnotationReader to use, duck-typed.
  47.      * @param string|string[]|null $paths  One or multiple paths where mapping classes can be found.
  48.      */
  49.     public function __construct($reader$paths null)
  50.     {
  51.         $this->reader $reader;
  52.         $this->addPaths((array) $paths);
  53.     }
  54.     public function isTransient($className)
  55.     {
  56.         $classAnnotations $this->reader->getClassAnnotations(new ReflectionClass($className));
  57.         foreach ($classAnnotations as $annot) {
  58.             if ($annot instanceof ODM\AbstractDocument) {
  59.                 return false;
  60.             }
  61.         }
  62.         return true;
  63.     }
  64.     public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\ClassMetadata $metadata): void
  65.     {
  66.         assert($metadata instanceof ClassMetadata);
  67.         $reflClass $metadata->getReflectionClass();
  68.         $classAnnotations $this->reader->getClassAnnotations($reflClass);
  69.         $documentAnnot null;
  70.         foreach ($classAnnotations as $annot) {
  71.             $classAnnotations[get_class($annot)] = $annot;
  72.             if ($annot instanceof ODM\AbstractDocument) {
  73.                 if ($documentAnnot !== null) {
  74.                     throw MappingException::classCanOnlyBeMappedByOneAbstractDocument($className$documentAnnot$annot);
  75.                 }
  76.                 $documentAnnot $annot;
  77.             }
  78.             // non-document class annotations
  79.             if ($annot instanceof ODM\AbstractIndex) {
  80.                 $this->addIndex($metadata$annot);
  81.             }
  82.             if ($annot instanceof ODM\Indexes) {
  83.                 trigger_deprecation(
  84.                     'doctrine/mongodb-odm',
  85.                     '2.2',
  86.                     'The "@Indexes" annotation used in class "%s" is deprecated. Specify all "@Index" and "@UniqueIndex" annotations on the class.',
  87.                     $className,
  88.                 );
  89.                 $value $annot->value;
  90.                 foreach (is_array($value) ? $value : [$value] as $index) {
  91.                     $this->addIndex($metadata$index);
  92.                 }
  93.             } elseif ($annot instanceof ODM\InheritanceType) {
  94.                 $metadata->setInheritanceType(constant(ClassMetadata::class . '::INHERITANCE_TYPE_' $annot->value));
  95.             } elseif ($annot instanceof ODM\DiscriminatorField) {
  96.                 $metadata->setDiscriminatorField($annot->value);
  97.             } elseif ($annot instanceof ODM\DiscriminatorMap) {
  98.                 $value $annot->value;
  99.                 assert(is_array($value));
  100.                 $metadata->setDiscriminatorMap($value);
  101.             } elseif ($annot instanceof ODM\DiscriminatorValue) {
  102.                 $metadata->setDiscriminatorValue($annot->value);
  103.             } elseif ($annot instanceof ODM\ChangeTrackingPolicy) {
  104.                 $metadata->setChangeTrackingPolicy(constant(ClassMetadata::class . '::CHANGETRACKING_' $annot->value));
  105.             } elseif ($annot instanceof ODM\DefaultDiscriminatorValue) {
  106.                 $metadata->setDefaultDiscriminatorValue($annot->value);
  107.             } elseif ($annot instanceof ODM\ReadPreference) {
  108.                 $metadata->setReadPreference($annot->value$annot->tags ?? []);
  109.             } elseif ($annot instanceof ODM\Validation) {
  110.                 if (isset($annot->validator)) {
  111.                     try {
  112.                         $validatorBson fromJSON($annot->validator);
  113.                     } catch (UnexpectedValueException $e) {
  114.                         throw MappingException::schemaValidationError($e->getCode(), $e->getMessage(), $className'validator');
  115.                     }
  116.                     $validator toPHP($validatorBson, []);
  117.                     $metadata->setValidator($validator);
  118.                 }
  119.                 if (isset($annot->action)) {
  120.                     $metadata->setValidationAction($annot->action);
  121.                 }
  122.                 if (isset($annot->level)) {
  123.                     $metadata->setValidationLevel($annot->level);
  124.                 }
  125.             }
  126.         }
  127.         if ($documentAnnot === null) {
  128.             throw MappingException::classIsNotAValidDocument($className);
  129.         }
  130.         if ($documentAnnot instanceof ODM\MappedSuperclass) {
  131.             $metadata->isMappedSuperclass true;
  132.         } elseif ($documentAnnot instanceof ODM\EmbeddedDocument) {
  133.             $metadata->isEmbeddedDocument true;
  134.         } elseif ($documentAnnot instanceof ODM\QueryResultDocument) {
  135.             $metadata->isQueryResultDocument true;
  136.         } elseif ($documentAnnot instanceof ODM\View) {
  137.             if (! $documentAnnot->rootClass) {
  138.                 throw MappingException::viewWithoutRootClass($className);
  139.             }
  140.             if (! class_exists($documentAnnot->rootClass)) {
  141.                 throw MappingException::viewRootClassNotFound($className$documentAnnot->rootClass);
  142.             }
  143.             $metadata->markViewOf($documentAnnot->rootClass);
  144.         } elseif ($documentAnnot instanceof ODM\File) {
  145.             $metadata->isFile true;
  146.             if ($documentAnnot->chunkSizeBytes !== null) {
  147.                 $metadata->setChunkSizeBytes($documentAnnot->chunkSizeBytes);
  148.             }
  149.         }
  150.         if (isset($documentAnnot->db)) {
  151.             $metadata->setDatabase($documentAnnot->db);
  152.         }
  153.         if (isset($documentAnnot->collection)) {
  154.             $metadata->setCollection($documentAnnot->collection);
  155.         }
  156.         if (isset($documentAnnot->view)) {
  157.             $metadata->setCollection($documentAnnot->view);
  158.         }
  159.         // Store bucketName as collection name for GridFS files
  160.         if (isset($documentAnnot->bucketName)) {
  161.             $metadata->setBucketName($documentAnnot->bucketName);
  162.         }
  163.         if (isset($documentAnnot->repositoryClass)) {
  164.             $metadata->setCustomRepositoryClass($documentAnnot->repositoryClass);
  165.         }
  166.         if (isset($documentAnnot->writeConcern)) {
  167.             $metadata->setWriteConcern($documentAnnot->writeConcern);
  168.         }
  169.         if (isset($documentAnnot->indexes) && count($documentAnnot->indexes)) {
  170.             trigger_deprecation(
  171.                 'doctrine/mongodb-odm',
  172.                 '2.2',
  173.                 'The "indexes" parameter in the "%s" annotation for class "%s" is deprecated. Specify all "@Index" and "@UniqueIndex" annotations on the class.',
  174.                 $className,
  175.                 get_class($documentAnnot),
  176.             );
  177.             foreach ($documentAnnot->indexes as $index) {
  178.                 $this->addIndex($metadata$index);
  179.             }
  180.         }
  181.         if (! empty($documentAnnot->readOnly)) {
  182.             $metadata->markReadOnly();
  183.         }
  184.         foreach ($reflClass->getProperties() as $property) {
  185.             if (
  186.                 ($metadata->isMappedSuperclass && ! $property->isPrivate())
  187.                 ||
  188.                 ($metadata->isInheritedField($property->name) && $property->getDeclaringClass()->name !== $metadata->name)
  189.             ) {
  190.                 continue;
  191.             }
  192.             $indexes    = [];
  193.             $mapping    = ['fieldName' => $property->getName()];
  194.             $fieldAnnot null;
  195.             foreach ($this->reader->getPropertyAnnotations($property) as $annot) {
  196.                 if ($annot instanceof ODM\AbstractField) {
  197.                     $fieldAnnot $annot;
  198.                 }
  199.                 if ($annot instanceof ODM\AbstractIndex) {
  200.                     $indexes[] = $annot;
  201.                 }
  202.                 if ($annot instanceof ODM\Indexes) {
  203.                     $value $annot->value;
  204.                     foreach (is_array($value) ? $value : [$value] as $index) {
  205.                         $indexes[] = $index;
  206.                     }
  207.                 } elseif ($annot instanceof ODM\AlsoLoad) {
  208.                     $mapping['alsoLoadFields'] = (array) $annot->value;
  209.                 } elseif ($annot instanceof ODM\Version) {
  210.                     $mapping['version'] = true;
  211.                 } elseif ($annot instanceof ODM\Lock) {
  212.                     $mapping['lock'] = true;
  213.                 }
  214.             }
  215.             if ($fieldAnnot) {
  216.                 $mapping array_replace($mapping, (array) $fieldAnnot);
  217.                 $metadata->mapField($mapping);
  218.             }
  219.             if (! $indexes) {
  220.                 continue;
  221.             }
  222.             foreach ($indexes as $index) {
  223.                 $name $mapping['name'] ?? $mapping['fieldName'];
  224.                 $keys = [$name => $index->order ?: 'asc'];
  225.                 $this->addIndex($metadata$index$keys);
  226.             }
  227.         }
  228.         // Set shard key after all fields to ensure we mapped all its keys
  229.         if (isset($classAnnotations[ShardKey::class])) {
  230.             assert($classAnnotations[ShardKey::class] instanceof ShardKey);
  231.             $this->setShardKey($metadata$classAnnotations[ShardKey::class]);
  232.         }
  233.         foreach ($reflClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
  234.             /* Filter for the declaring class only. Callbacks from parent
  235.              * classes will already be registered.
  236.              */
  237.             if ($method->getDeclaringClass()->name !== $reflClass->name) {
  238.                 continue;
  239.             }
  240.             foreach ($this->reader->getMethodAnnotations($method) as $annot) {
  241.                 if ($annot instanceof ODM\AlsoLoad) {
  242.                     $metadata->registerAlsoLoadMethod($method->getName(), $annot->value);
  243.                 }
  244.                 if (! isset($classAnnotations[ODM\HasLifecycleCallbacks::class])) {
  245.                     continue;
  246.                 }
  247.                 if ($annot instanceof ODM\PrePersist) {
  248.                     $metadata->addLifecycleCallback($method->getName(), Events::prePersist);
  249.                 } elseif ($annot instanceof ODM\PostPersist) {
  250.                     $metadata->addLifecycleCallback($method->getName(), Events::postPersist);
  251.                 } elseif ($annot instanceof ODM\PreUpdate) {
  252.                     $metadata->addLifecycleCallback($method->getName(), Events::preUpdate);
  253.                 } elseif ($annot instanceof ODM\PostUpdate) {
  254.                     $metadata->addLifecycleCallback($method->getName(), Events::postUpdate);
  255.                 } elseif ($annot instanceof ODM\PreRemove) {
  256.                     $metadata->addLifecycleCallback($method->getName(), Events::preRemove);
  257.                 } elseif ($annot instanceof ODM\PostRemove) {
  258.                     $metadata->addLifecycleCallback($method->getName(), Events::postRemove);
  259.                 } elseif ($annot instanceof ODM\PreLoad) {
  260.                     $metadata->addLifecycleCallback($method->getName(), Events::preLoad);
  261.                 } elseif ($annot instanceof ODM\PostLoad) {
  262.                     $metadata->addLifecycleCallback($method->getName(), Events::postLoad);
  263.                 } elseif ($annot instanceof ODM\PreFlush) {
  264.                     $metadata->addLifecycleCallback($method->getName(), Events::preFlush);
  265.                 }
  266.             }
  267.         }
  268.     }
  269.     /**
  270.      * @param ClassMetadata<object>     $class
  271.      * @param array<string, int|string> $keys
  272.      */
  273.     private function addIndex(ClassMetadata $classAbstractIndex $index, array $keys = []): void
  274.     {
  275.         $keys    array_merge($keys$index->keys);
  276.         $options = [];
  277.         $allowed = ['name''background''unique''sparse''expireAfterSeconds'];
  278.         foreach ($allowed as $name) {
  279.             if (! isset($index->$name)) {
  280.                 continue;
  281.             }
  282.             $options[$name] = $index->$name;
  283.         }
  284.         if (! empty($index->partialFilterExpression)) {
  285.             $options['partialFilterExpression'] = $index->partialFilterExpression;
  286.         }
  287.         $options array_merge($options$index->options);
  288.         $class->addIndex($keys$options);
  289.     }
  290.     /**
  291.      * @param ClassMetadata<object> $class
  292.      *
  293.      * @throws MappingException
  294.      */
  295.     private function setShardKey(ClassMetadata $classODM\ShardKey $shardKey): void
  296.     {
  297.         $options = [];
  298.         $allowed = ['unique''numInitialChunks'];
  299.         foreach ($allowed as $name) {
  300.             if (! isset($shardKey->$name)) {
  301.                 continue;
  302.             }
  303.             $options[$name] = $shardKey->$name;
  304.         }
  305.         $class->setShardKey($shardKey->keys$options);
  306.     }
  307.     /**
  308.      * Retrieve the current annotation reader
  309.      *
  310.      * @return Reader
  311.      */
  312.     public function getReader()
  313.     {
  314.         trigger_deprecation(
  315.             'doctrine/mongodb-odm',
  316.             '2.4',
  317.             '%s is deprecated with no replacement',
  318.             __METHOD__,
  319.         );
  320.         return $this->reader;
  321.     }
  322.     /**
  323.      * Factory method for the Annotation Driver
  324.      *
  325.      * @param string[]|string $paths
  326.      */
  327.     public static function create($paths = [], ?Reader $reader null): AnnotationDriver
  328.     {
  329.         return new self($reader ?? new AnnotationReader(), $paths);
  330.     }
  331. }
  332. interface_exists(\Doctrine\Persistence\Mapping\ClassMetadata::class);