/** * Validate runtime metadata is correctly defined. * * @param ClassMetadata $class * @param ClassMetadataInterface|null $parent * * @return void * * @throws MappingException */ protected function validateRuntimeMetadata($class, $parent) { if (!$class->reflClass) { // only validate if there is a reflection class instance return; } $class->validateIdentifier(); $class->validateAssociations(); $class->validateLifecycleCallbacks($this->getReflectionService()); // verify inheritance if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { if (!$parent) { if (count($class->discriminatorMap) == 0) { throw MappingException::missingDiscriminatorMap($class->name); } if (!$class->discriminatorColumn) { throw MappingException::missingDiscriminatorColumn($class->name); } } else { if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { // enforce discriminator map for all entities of an inheritance hierarchy, otherwise problems will occur. throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); } } } else { if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy throw MappingException::noInheritanceOnMappedSuperClass($class->name); } } }
/** * Loads the metadata of the class in question and all it's ancestors whose metadata * is still not loaded. * * @param string $name The name of the class for which the metadata should get loaded. * @param array $tables The metadata collection to which the loaded metadata is added. */ protected function loadMetadata($name) { if (!$this->initialized) { $this->initialize(); } $loaded = array(); $parentClasses = $this->getParentClasses($name); $parentClasses[] = $name; // Move down the hierarchy of parent classes, starting from the topmost class $parent = null; $rootEntityFound = false; $visited = array(); foreach ($parentClasses as $className) { if (isset($this->loadedMetadata[$className])) { $parent = $this->loadedMetadata[$className]; if (!$parent->isMappedSuperclass) { $rootEntityFound = true; array_unshift($visited, $className); } continue; } $class = $this->newClassMetadataInstance($className); if ($parent) { $class->setInheritanceType($parent->inheritanceType); $class->setDiscriminatorColumn($parent->discriminatorColumn); $class->setIdGeneratorType($parent->generatorType); $this->addInheritedFields($class, $parent); $this->addInheritedRelations($class, $parent); $class->setIdentifier($parent->identifier); $class->setVersioned($parent->isVersioned); $class->setVersionField($parent->versionField); $class->setDiscriminatorMap($parent->discriminatorMap); $class->setLifecycleCallbacks($parent->lifecycleCallbacks); $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); } // Invoke driver try { $this->driver->loadMetadataForClass($className, $class); } catch (ReflectionException $e) { throw MappingException::reflectionFailure($className, $e); } // If this class has a parent the id generator strategy is inherited. // However this is only true if the hierachy of parents contains the root entity, // if it consinsts of mapped superclasses these don't necessarily include the id field. if ($parent && $rootEntityFound) { if ($parent->isIdGeneratorSequence()) { $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); } else { if ($parent->isIdGeneratorTable()) { $class->getTableGeneratorDefinition($parent->tableGeneratorDefinition); } } if ($parent->generatorType) { $class->setIdGeneratorType($parent->generatorType); } if ($parent->idGenerator) { $class->setIdGenerator($parent->idGenerator); } } else { $this->completeIdGeneratorMapping($class); } if ($parent && $parent->isInheritanceTypeSingleTable()) { $class->setPrimaryTable($parent->table); } $class->setParentClasses($visited); if ($this->evm->hasListeners(Events::loadClassMetadata)) { $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); } // Verify & complete identifier mapping if (!$class->identifier && !$class->isMappedSuperclass) { throw MappingException::identifierRequired($className); } // verify inheritance if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { if (!$parent) { if (count($class->discriminatorMap) == 0) { throw MappingException::missingDiscriminatorMap($class->name); } if (!$class->discriminatorColumn) { throw MappingException::missingDiscriminatorColumn($class->name); } } else { if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { // enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur. throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); } } } else { if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { // second condition is necessary for mapped superclasses in the middle of an inheritance hierachy throw MappingException::noInheritanceOnMappedSuperClass($class->name); } } $this->loadedMetadata[$className] = $class; $parent = $class; if (!$class->isMappedSuperclass) { $rootEntityFound = true; array_unshift($visited, $className); } $loaded[] = $className; } return $loaded; }
/** * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator * map classes and looking for a fitting one. * * @param ClassMetadata $metadata * * @return void * * @throws MappingException */ private function resolveDiscriminatorValue(ClassMetadata $metadata) { if ($metadata->discriminatorValue || !$metadata->discriminatorMap || $metadata->isMappedSuperclass || !$metadata->reflClass || $metadata->reflClass->isAbstract()) { return; } // minor optimization: avoid loading related metadata when not needed foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { if ($discriminatorClass === $metadata->name) { $metadata->discriminatorValue = $discriminatorValue; return; } } // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) { $metadata->discriminatorValue = $discriminatorValue; return; } } throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName); }
/** * Validate runtime metadata is correctly defined. * * @param ClassMetadata $class * @param ClassMetadata $parent */ protected function validateRuntimeMetadata($class, $parent) { // Verify & complete identifier mapping if (!$class->identifier && !$class->isMappedSuperclass) { throw MappingException::identifierRequired($class->name); } // verify inheritance if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { if (!$parent) { if (count($class->discriminatorMap) == 0) { throw MappingException::missingDiscriminatorMap($class->name); } if (!$class->discriminatorColumn) { throw MappingException::missingDiscriminatorColumn($class->name); } } else { if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { // enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur. throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); } } } else { if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { // second condition is necessary for mapped superclasses in the middle of an inheritance hierachy throw MappingException::noInheritanceOnMappedSuperClass($class->name); } } }