/**
  * @param ClassMetadata $metadata metadata
  *
  * @return ClassMetadata|null
  */
 public function findReferenceFor(ClassMetadata $metadata)
 {
     $table = $metadata->getTableName();
     $references = $this->getReferencesForTable($table);
     if (empty($references)) {
         return;
     }
     if ($metadata->isInheritanceTypeNone()) {
         if (count($references) > 1) {
             throw new Exception\UnsupportedException(sprintf('You use different inheritance type on table %s', $table));
         }
         return current($references);
     }
     if (false === $metadata->isInheritanceTypeSingleTable()) {
         throw new Exception\UnsupportedException('Only “single_table“ inheritance type is supported by Rezzza\\DoctrineSchemaMultiMapping at this moment.');
     }
     $discriminatorValue = $metadata->discriminatorValue;
     $discriminatorColumn = $metadata->discriminatorColumn;
     foreach ($references as $reference) {
         if ($discriminatorColumn != $reference->discriminatorColumn) {
             throw new \LogicException(sprintf('You have 2 tables with inherhitance Single Table with a different discriminatorColumn on table "%s"', $table));
         }
         if ($discriminatorValue == $reference->discriminatorValue) {
             return $reference;
         }
     }
 }
 /**
  * Collecting discriminator map entries from child classes for entities with inheritance not equals NONE
  *
  * @param ClassMetadata $class
  * @param EntityManager $em
  *
  * @throws MappingException
  */
 protected function processDiscriminatorValues(ClassMetadata $class, EntityManager $em)
 {
     if (!$class->isInheritanceTypeNone()) {
         if ($class->isRootEntity()) {
             $allClasses = $em->getConfiguration()->getMetadataDriverImpl()->getAllClassNames();
             $FQCN = $class->getName();
             $map = $class->discriminatorMap ?: [];
             $duplicates = [];
             foreach ($allClasses as $subClassCandidate) {
                 if (is_subclass_of($subClassCandidate, $FQCN) && !in_array($subClassCandidate, $map, true)) {
                     $value = $this->getDiscriminatorValue($em->getMetadataFactory(), $subClassCandidate);
                     if (null !== $value) {
                         if (isset($map[$value])) {
                             $duplicates[] = $value;
                         }
                         $map[$value] = $subClassCandidate;
                     }
                 }
             }
             if ($duplicates) {
                 throw MappingException::duplicateDiscriminatorEntry($class->getName(), $duplicates, $map);
             }
             $class->setDiscriminatorMap($map);
             $this->collectedMaps = array_merge($this->collectedMaps, array_fill_keys(array_values($map), $map));
         } elseif (isset($this->collectedMaps[$class->name]) && $class->discriminatorMap !== $this->collectedMaps[$class->name]) {
             $class->setDiscriminatorMap($this->collectedMaps[$class->name]);
         }
     }
 }
Exemple #3
0
 /**
  * INTERNAL:
  * Computes the changeset of an individual entity, independently of the
  * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
  *
  * The passed entity must be a managed entity. If the entity already has a change set
  * because this method is invoked during a commit cycle then the change sets are added.
  * whereby changes detected in this method prevail.
  *
  * @ignore
  *
  * @param ClassMetadata $class  The class descriptor of the entity.
  * @param object        $entity The entity for which to (re)calculate the change set.
  *
  * @return void
  *
  * @throws ORMInvalidArgumentException If the passed entity is not MANAGED.
  */
 public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
 {
     $oid = spl_object_hash($entity);
     if (!isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
         throw ORMInvalidArgumentException::entityNotManaged($entity);
     }
     // skip if change tracking is "NOTIFY"
     if ($class->isChangeTrackingNotify()) {
         return;
     }
     if (!$class->isInheritanceTypeNone()) {
         $class = $this->em->getClassMetadata(get_class($entity));
     }
     $actualData = array();
     foreach ($class->reflFields as $name => $refProp) {
         if ((!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) && $name !== $class->versionField && !$class->isCollectionValuedAssociation($name)) {
             $actualData[$name] = $refProp->getValue($entity);
         }
     }
     if (!isset($this->originalEntityData[$oid])) {
         throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
     }
     $originalData = $this->originalEntityData[$oid];
     $changeSet = array();
     foreach ($actualData as $propName => $actualValue) {
         $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
         if ($orgValue !== $actualValue) {
             $changeSet[$propName] = array($orgValue, $actualValue);
         }
     }
     if ($changeSet) {
         $this->entityChangeSets[$oid] = isset($this->entityChangeSets[$oid]) ? array_merge($this->entityChangeSets[$oid], $changeSet) : $changeSet;
         $this->originalEntityData[$oid] = $actualData;
         $this->entityUpdates[$oid] = $entity;
     }
 }
Exemple #4
0
 /**
  * INTERNAL:
  * Computes the changeset of an individual entity, independently of the
  * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit().
  * 
  * The passed entity must be a managed entity. If the entity already has a change set
  * because this method is invoked during a commit cycle then the change sets are added.
  * whereby changes detected in this method prevail.
  * 
  * @ignore
  * @param ClassMetadata $class The class descriptor of the entity.
  * @param object $entity The entity for which to (re)calculate the change set.
  * @throws InvalidArgumentException If the passed entity is not MANAGED.
  */
 public function recomputeSingleEntityChangeSet($class, $entity)
 {
     $oid = spl_object_hash($entity);
     if (!isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) {
         throw new InvalidArgumentException('Entity must be managed.');
     }
     /* TODO: Just return if changetracking policy is NOTIFY?
        if ($class->isChangeTrackingNotify()) {
            return;
        }*/
     if (!$class->isInheritanceTypeNone()) {
         $class = $this->em->getClassMetadata(get_class($entity));
     }
     $actualData = array();
     foreach ($class->reflFields as $name => $refProp) {
         if (!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) {
             $actualData[$name] = $refProp->getValue($entity);
         }
     }
     $originalData = $this->originalEntityData[$oid];
     $changeSet = array();
     foreach ($actualData as $propName => $actualValue) {
         $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
         if (is_object($orgValue) && $orgValue !== $actualValue) {
             $changeSet[$propName] = array($orgValue, $actualValue);
         } else {
             if ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) {
                 $changeSet[$propName] = array($orgValue, $actualValue);
             }
         }
     }
     if ($changeSet) {
         if (isset($this->entityChangeSets[$oid])) {
             $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet);
         }
         $this->originalEntityData[$oid] = $actualData;
     }
 }
 /**
  * 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);
         }
     }
 }
Exemple #6
0
 /**
  * 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);
             }
         }
     } elseif ($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);
     }
 }
 /**
  * Computes the changes that happened to a single entity.
  *
  * Modifies/populates the following properties:
  *
  * {@link _originalEntityData}
  * If the entity is NEW or MANAGED but not yet fully persisted (only has an id)
  * then it was not fetched from the database and therefore we have no original
  * entity data yet. All of the current entity data is stored as the original entity data.
  *
  * {@link _entityChangeSets}
  * The changes detected on all properties of the entity are stored there.
  * A change is a tuple array where the first entry is the old value and the second
  * entry is the new value of the property. Changesets are used by persisters
  * to INSERT/UPDATE the persistent entity state.
  *
  * {@link _entityUpdates}
  * If the entity is already fully MANAGED (has been fetched from the database before)
  * and any changes to its properties are detected, then a reference to the entity is stored
  * there to mark it for an update.
  *
  * {@link _collectionDeletions}
  * If a PersistentCollection has been de-referenced in a fully MANAGED entity,
  * then this collection is marked for deletion.
  *
  * @param ClassMetadata $class The class descriptor of the entity.
  * @param object $entity The entity for which to compute the changes.
  */
 public function computeChangeSet(Mapping\ClassMetadata $class, $entity)
 {
     if (!$class->isInheritanceTypeNone()) {
         $class = $this->_em->getClassMetadata(get_class($entity));
     }
     $oid = spl_object_hash($entity);
     $actualData = array();
     foreach ($class->reflFields as $name => $refProp) {
         if (!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) {
             $actualData[$name] = $refProp->getValue($entity);
         }
         if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null && !$actualData[$name] instanceof PersistentCollection) {
             // If $actualData[$name] is not a Collection then use an ArrayCollection.
             if (!$actualData[$name] instanceof Collection) {
                 $actualData[$name] = new ArrayCollection($actualData[$name]);
             }
             $assoc = $class->associationMappings[$name];
             // Inject PersistentCollection
             $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $actualData[$name]);
             $coll->setOwner($entity, $assoc);
             $coll->setDirty(!$coll->isEmpty());
             $class->reflFields[$name]->setValue($entity, $coll);
             $actualData[$name] = $coll;
         }
     }
     if (!isset($this->_originalEntityData[$oid])) {
         // Entity is either NEW or MANAGED but not yet fully persisted (only has an id).
         // These result in an INSERT.
         $this->_originalEntityData[$oid] = $actualData;
         $this->_entityChangeSets[$oid] = array_map(function ($e) {
             return array(null, $e);
         }, $actualData);
     } else {
         // Entity is "fully" MANAGED: it was already fully persisted before
         // and we have a copy of the original data
         $originalData = $this->_originalEntityData[$oid];
         $changeSet = array();
         $entityIsDirty = false;
         foreach ($actualData as $propName => $actualValue) {
             $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
             if (is_object($orgValue) && $orgValue !== $actualValue) {
                 $changeSet[$propName] = array($orgValue, $actualValue);
             } else {
                 if ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) {
                     $changeSet[$propName] = array($orgValue, $actualValue);
                 }
             }
             if (isset($changeSet[$propName])) {
                 if (isset($class->associationMappings[$propName])) {
                     $assoc = $class->associationMappings[$propName];
                     if ($assoc->isOneToOne()) {
                         if ($assoc->isOwningSide) {
                             $entityIsDirty = true;
                         }
                         if ($actualValue === null && $assoc->orphanRemoval) {
                             $this->scheduleOrphanRemoval($orgValue);
                         }
                     } else {
                         if ($orgValue instanceof PersistentCollection) {
                             // A PersistentCollection was de-referenced, so delete it.
                             if (!in_array($orgValue, $this->_collectionDeletions, true)) {
                                 $this->_collectionDeletions[] = $orgValue;
                             }
                         }
                     }
                 } else {
                     $entityIsDirty = true;
                 }
             }
         }
         if ($changeSet) {
             $this->_entityChangeSets[$oid] = $changeSet;
             $this->_originalEntityData[$oid] = $actualData;
             if ($entityIsDirty) {
                 $this->_entityUpdates[$oid] = $entity;
             }
         }
     }
     // Look for changes in associations of the entity
     foreach ($class->associationMappings as $assoc) {
         $val = $class->reflFields[$assoc->sourceFieldName]->getValue($entity);
         if ($val !== null) {
             $this->_computeAssociationChanges($assoc, $val);
         }
     }
 }
 /**
  * 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);
         }
     }
 }