/** * Adds and embedded class * * @param string $fieldName * @param string $class * @param string|null $columnPrefix * * @return $this */ public function addEmbedded($fieldName, $class, $columnPrefix = null) { $this->cm->mapEmbedded(['fieldName' => $fieldName, 'class' => $class, 'columnPrefix' => $columnPrefix]); return $this; }
/** * @param string $fieldName * @param ClassMetadataInfo $classMetadata * @param ClassMetadataInfo $embeddableMetadata */ private function mapNestedEmbedded($fieldName, ClassMetadataInfo $classMetadata, ClassMetadataInfo $embeddableMetadata) { foreach ($embeddableMetadata->embeddedClasses as $property => $embeddableClass) { $classMetadata->mapEmbedded(array('fieldName' => $fieldName . '.' . $property, 'class' => $embeddableClass['class'], 'columnPrefix' => $embeddableClass['columnPrefix'], 'declaredField' => $embeddableClass['declaredField'] ? $fieldName . '.' . $embeddableClass['declaredField'] : $fieldName, 'originalField' => $embeddableClass['originalField'] ?: $property)); } }
/** * Evaluate the property annotations and amend the metadata accordingly. * * @param ORM\ClassMetadataInfo $metadata * @return void * @throws ORM\MappingException */ protected function evaluatePropertyAnnotations(ORM\ClassMetadataInfo $metadata) { $className = $metadata->name; $class = $metadata->getReflectionClass(); $classSchema = $this->getClassSchema($className); foreach ($class->getProperties() as $property) { if (!$classSchema->hasProperty($property->getName()) || $classSchema->isPropertyTransient($property->getName()) || $metadata->isMappedSuperclass && !$property->isPrivate() || $metadata->isInheritedField($property->getName()) || $metadata->isInheritedAssociation($property->getName()) || $metadata->isInheritedEmbeddedClass($property->getName())) { continue; } $propertyMetaData = $classSchema->getProperty($property->getName()); $mapping = []; $mapping['fieldName'] = $property->getName(); $mapping['columnName'] = strtolower($property->getName()); $mapping['targetEntity'] = $propertyMetaData['type']; $joinColumns = $this->evaluateJoinColumnAnnotations($property); // Field can only be annotated with one of: // @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Column (optional) if ($oneToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToOne::class)) { if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($oneToOneAnnotation->targetEntity) { $mapping['targetEntity'] = $oneToOneAnnotation->targetEntity; } if ($oneToOneAnnotation->inversedBy !== null || $oneToOneAnnotation->mappedBy === null) { $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property); } $mapping['mappedBy'] = $oneToOneAnnotation->mappedBy; $mapping['inversedBy'] = $oneToOneAnnotation->inversedBy; if ($oneToOneAnnotation->cascade) { $mapping['cascade'] = $oneToOneAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } if ($oneToOneAnnotation->orphanRemoval) { $mapping['orphanRemoval'] = $oneToOneAnnotation->orphanRemoval; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) { $mapping['orphanRemoval'] = true; } $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnotation->fetch); $metadata->mapOneToOne($mapping); } elseif ($oneToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OneToMany::class)) { $mapping['mappedBy'] = $oneToManyAnnotation->mappedBy; if ($oneToManyAnnotation->targetEntity) { $mapping['targetEntity'] = $oneToManyAnnotation->targetEntity; } elseif (isset($propertyMetaData['elementType'])) { $mapping['targetEntity'] = $propertyMetaData['elementType']; } if ($oneToManyAnnotation->cascade) { $mapping['cascade'] = $oneToManyAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['indexBy'] = $oneToManyAnnotation->indexBy; if ($oneToManyAnnotation->orphanRemoval) { $mapping['orphanRemoval'] = $oneToManyAnnotation->orphanRemoval; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false && $this->isValueObject($mapping['targetEntity'], $className) === false) { $mapping['orphanRemoval'] = true; } $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnotation->fetch); if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) { $mapping['orderBy'] = $orderByAnnotation->value; } $metadata->mapOneToMany($mapping); } elseif ($manyToOneAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToOne::class)) { if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($manyToOneAnnotation->targetEntity) { $mapping['targetEntity'] = $manyToOneAnnotation->targetEntity; } $mapping['joinColumns'] = $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property); if ($manyToOneAnnotation->cascade) { $mapping['cascade'] = $manyToOneAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['inversedBy'] = $manyToOneAnnotation->inversedBy; $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnotation->fetch); $metadata->mapManyToOne($mapping); } elseif ($manyToManyAnnotation = $this->reader->getPropertyAnnotation($property, ORM\ManyToMany::class)) { if ($manyToManyAnnotation->targetEntity) { $mapping['targetEntity'] = $manyToManyAnnotation->targetEntity; } elseif (isset($propertyMetaData['elementType'])) { $mapping['targetEntity'] = $propertyMetaData['elementType']; } /** @var ORM\JoinTable $joinTableAnnotation */ if ($joinTableAnnotation = $this->reader->getPropertyAnnotation($property, ORM\JoinTable::class)) { $joinTable = $this->evaluateJoinTableAnnotation($joinTableAnnotation, $property, $className, $mapping); } else { $joinColumns = [['name' => null, 'referencedColumnName' => null]]; $joinTable = ['name' => $this->inferJoinTableNameFromClassAndPropertyName($className, $property->getName()), 'joinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property, self::MAPPING_MM_REGULAR), 'inverseJoinColumns' => $this->buildJoinColumnsIfNeeded($joinColumns, $mapping, $property)]; } $mapping['joinTable'] = $joinTable; $mapping['mappedBy'] = $manyToManyAnnotation->mappedBy; $mapping['inversedBy'] = $manyToManyAnnotation->inversedBy; if ($manyToManyAnnotation->cascade) { $mapping['cascade'] = $manyToManyAnnotation->cascade; } elseif ($this->isValueObject($mapping['targetEntity'], $className)) { $mapping['cascade'] = ['persist']; } elseif ($this->isAggregateRoot($mapping['targetEntity'], $className) === false) { $mapping['cascade'] = ['all']; } $mapping['indexBy'] = $manyToManyAnnotation->indexBy; $mapping['orphanRemoval'] = $manyToManyAnnotation->orphanRemoval; $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnotation->fetch); if ($orderByAnnotation = $this->reader->getPropertyAnnotation($property, ORM\OrderBy::class)) { $mapping['orderBy'] = $orderByAnnotation->value; } $metadata->mapManyToMany($mapping); } elseif ($embeddedAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Embedded::class)) { if ($embeddedAnnotation->class) { $mapping['class'] = $embeddedAnnotation->class; } else { // This will not happen currently, because "class" argument is required. It would be nice if that could be changed though. $mapping['class'] = $mapping['targetEntity']; } $mapping['columnPrefix'] = $embeddedAnnotation->columnPrefix; $metadata->mapEmbedded($mapping); } else { $mapping['nullable'] = false; /** @var ORM\Column $columnAnnotation */ if ($columnAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Column::class)) { $mapping = $this->addColumnToMappingArray($columnAnnotation, $mapping); } if (!isset($mapping['type'])) { switch ($propertyMetaData['type']) { case 'DateTime': $mapping['type'] = 'datetime'; break; case 'string': case 'integer': case 'boolean': case 'float': case 'array': $mapping['type'] = $propertyMetaData['type']; break; default: if (strpos($propertyMetaData['type'], '\\') !== false) { if ($this->reflectionService->isClassAnnotatedWith($propertyMetaData['type'], Flow\ValueObject::class)) { $valueObjectAnnotation = $this->reflectionService->getClassAnnotation($propertyMetaData['type'], Flow\ValueObject::class); if ($valueObjectAnnotation->embedded === true) { $mapping['class'] = $propertyMetaData['type']; $mapping['columnPrefix'] = $mapping['columnName']; $metadata->mapEmbedded($mapping); // Leave switch and continue with next property continue 2; } $mapping['type'] = 'object'; } elseif (class_exists($propertyMetaData['type'])) { throw ORM\MappingException::missingRequiredOption($property->getName(), 'OneToOne', sprintf('The property "%s" in class "%s" has a non standard data type and doesn\'t define the type of the relation. You have to use one of these annotations: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany', $property->getName(), $className)); } } else { throw ORM\MappingException::propertyTypeIsRequired($className, $property->getName()); } } } if ($this->reader->getPropertyAnnotation($property, ORM\Id::class) !== null) { $mapping['id'] = true; } if ($generatedValueAnnotation = $this->reader->getPropertyAnnotation($property, ORM\GeneratedValue::class)) { $metadata->setIdGeneratorType(constant('Doctrine\\ORM\\Mapping\\ClassMetadata::GENERATOR_TYPE_' . strtoupper($generatedValueAnnotation->strategy))); } if ($this->reflectionService->isPropertyAnnotatedWith($className, $property->getName(), ORM\Version::class)) { $metadata->setVersionMapping($mapping); } $metadata->mapField($mapping); // Check for SequenceGenerator/TableGenerator definition if ($seqGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\SequenceGenerator::class)) { $metadata->setSequenceGeneratorDefinition(['sequenceName' => $seqGeneratorAnnotation->sequenceName, 'allocationSize' => $seqGeneratorAnnotation->allocationSize, 'initialValue' => $seqGeneratorAnnotation->initialValue]); } elseif ($this->reader->getPropertyAnnotation($property, ORM\TableGenerator::class) !== null) { throw ORM\MappingException::tableIdGeneratorNotImplemented($className); } elseif ($customGeneratorAnnotation = $this->reader->getPropertyAnnotation($property, ORM\CustomIdGenerator::class)) { $metadata->setCustomGeneratorDefinition(['class' => $customGeneratorAnnotation->class]); } } // Evaluate @Cache annotation if (($cacheAnnotation = $this->reader->getPropertyAnnotation($property, ORM\Cache::class)) !== null) { $metadata->enableAssociationCache($mapping['fieldName'], array('usage' => constant('Doctrine\\ORM\\Mapping\\ClassMetadata::CACHE_USAGE_' . $cacheAnnotation->usage), 'region' => $cacheAnnotation->region)); } } }