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));
}
}
}