/**
  * Set up the PersistentCollection used for collection initialization tests.
  */
 public function setUpPersistentCollection()
 {
     $classMetaData = $this->_emMock->getClassMetadata('Doctrine\\Tests\\Models\\ECommerce\\ECommerceCart');
     $this->collection = new PersistentCollection($this->_emMock, $classMetaData, new ArrayCollection());
     $this->collection->setInitialized(false);
     $this->collection->setOwner(new ECommerceCart(), $classMetaData->getAssociationMapping('products'));
 }
Beispiel #2
0
 public function testShouldNotScheduleDeletionOnClonedInstances()
 {
     $class = $this->_em->getClassMetadata('Doctrine\\Tests\\Models\\ECommerce\\ECommerceProduct');
     $product = new ECommerceProduct();
     $category = new ECommerceCategory();
     $collection = new PersistentCollection($this->_em, $class, new ArrayCollection(array($category)));
     $collection->setOwner($product, $class->associationMappings['categories']);
     $uow = $this->_em->getUnitOfWork();
     $clonedCollection = clone $collection;
     $clonedCollection->clear();
     $this->assertEquals(0, count($uow->getScheduledCollectionDeletions()));
 }
 /**
  * Prepare a lazy loadable PersistentCollection
  * on the entity to get Products.
  * The entity must have a "products" property defined
  *
  * @param object        $entity        The entity related to the products
  * @param array         $assoc         Association properties
  * @param EntityManager $entityManager Entity manager
  */
 protected function setProductPersistentCollection($entity, $assoc, EntityManager $entityManager)
 {
     $targetEntity = $this->productClass;
     $productsCollection = new PersistentCollection($entityManager, $targetEntity, new ArrayCollection());
     $assoc['fieldName'] = 'products';
     $assoc['targetEntity'] = $targetEntity;
     $assoc['type'] = ClassMetadata::MANY_TO_MANY;
     $assoc['inversedBy'] = '';
     $assoc['isOwningSide'] = false;
     $assoc['sourceEntity'] = get_class($entity);
     $assoc['orphanRemoval'] = false;
     $productsCollection->setOwner($entity, $assoc);
     $productsCollection->setInitialized(false);
     $entityMetadata = $entityManager->getClassMetadata(get_class($entity));
     $productsReflProp = $entityMetadata->reflClass->getProperty('products');
     $productsReflProp->setAccessible(true);
     $productsReflProp->setValue($entity, $productsCollection);
 }
 /**
  * Initializes a related collection.
  *
  * @param object        $entity         The entity to which the collection belongs.
  * @param ClassMetadata $class
  * @param string        $fieldName      The name of the field on the entity that holds the collection.
  * @param string        $parentDqlAlias Alias of the parent fetch joining this collection.
  *
  * @return \Doctrine\ORM\PersistentCollection
  */
 private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
 {
     $oid = spl_object_hash($entity);
     $relation = $class->associationMappings[$fieldName];
     $value = $class->reflFields[$fieldName]->getValue($entity);
     if ($value === null || is_array($value)) {
         $value = new ArrayCollection((array) $value);
     }
     if (!$value instanceof PersistentCollection) {
         $value = new PersistentCollection($this->_em, $this->_metadataCache[$relation['targetEntity']], $value);
         $value->setOwner($entity, $relation);
         $class->reflFields[$fieldName]->setValue($entity, $value);
         $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
         $this->initializedCollections[$oid . $fieldName] = $value;
     } else {
         if (isset($this->_hints[Query::HINT_REFRESH]) || isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && !$value->isInitialized()) {
             // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
             $value->setDirty(false);
             $value->setInitialized(true);
             $value->unwrap()->clear();
             $this->initializedCollections[$oid . $fieldName] = $value;
         } else {
             // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
             $this->existingCollections[$oid . $fieldName] = $value;
         }
     }
     return $value;
 }
 /**
  * @param object $owner
  * @param array  $mapping
  * @param array  $items
  *
  * @return PersistentCollection
  */
 protected function getPersistentCollection($owner, array $mapping, array $items = [])
 {
     $metadata = $this->getMockBuilder('Doctrine\\ORM\\Mapping\\ClassMetadata')->disableOriginalConstructor()->getMock();
     $coll = new PersistentCollection($this->em, $metadata, new ArrayCollection($items));
     $mapping['inversedBy'] = 'test';
     $coll->setOwner($owner, $mapping);
     return $coll;
 }
Beispiel #6
0
 /**
  * INTERNAL:
  * Creates an entity. Used for reconstitution of persistent entities.
  *
  * @ignore
  *
  * @param string $className The name of the entity class.
  * @param array  $data      The data for the entity.
  * @param array  $hints     Any hints to account for during reconstitution/lookup of the entity.
  *
  * @return object The managed entity instance.
  *
  * @internal Highly performance-sensitive method.
  *
  * @todo Rename: getOrCreateEntity
  */
 public function createEntity($className, array $data, &$hints = array())
 {
     $class = $this->em->getClassMetadata($className);
     //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
     if ($class->isIdentifierComposite) {
         $id = array();
         foreach ($class->identifier as $fieldName) {
             $id[$fieldName] = isset($class->associationMappings[$fieldName]) ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] : $data[$fieldName];
         }
     } else {
         $id = isset($class->associationMappings[$class->identifier[0]]) ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']] : $data[$class->identifier[0]];
         $id = array($class->identifier[0] => $id);
     }
     $idHash = implode(' ', $id);
     if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
         $entity = $this->identityMap[$class->rootEntityName][$idHash];
         $oid = spl_object_hash($entity);
         if (isset($hints[Query::HINT_REFRESH]) && isset($hints[Query::HINT_REFRESH_ENTITY]) && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity && $unmanagedProxy instanceof Proxy && $this->isIdentifierEquals($unmanagedProxy, $entity)) {
             // DDC-1238 - we have a managed instance, but it isn't the provided one.
             // Therefore we clear its identifier. Also, we must re-fetch metadata since the
             // refreshed object may be anything
             foreach ($class->identifier as $fieldName) {
                 $class->reflFields[$fieldName]->setValue($unmanagedProxy, null);
             }
             return $unmanagedProxy;
         }
         if ($entity instanceof Proxy && !$entity->__isInitialized()) {
             $entity->__setInitialized(true);
             $overrideLocalValues = true;
             if ($entity instanceof NotifyPropertyChanged) {
                 $entity->addPropertyChangedListener($this);
             }
         } else {
             $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
             // If only a specific entity is set to refresh, check that it's the one
             if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
                 $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
             }
         }
         if ($overrideLocalValues) {
             // inject ObjectManager upon refresh.
             if ($entity instanceof ObjectManagerAware) {
                 $entity->injectObjectManager($this->em, $class);
             }
             $this->originalEntityData[$oid] = $data;
         }
     } else {
         $entity = $this->newInstance($class);
         $oid = spl_object_hash($entity);
         $this->entityIdentifiers[$oid] = $id;
         $this->entityStates[$oid] = self::STATE_MANAGED;
         $this->originalEntityData[$oid] = $data;
         $this->identityMap[$class->rootEntityName][$idHash] = $entity;
         if ($entity instanceof NotifyPropertyChanged) {
             $entity->addPropertyChangedListener($this);
         }
         $overrideLocalValues = true;
     }
     if (!$overrideLocalValues) {
         return $entity;
     }
     foreach ($data as $field => $value) {
         if (isset($class->fieldMappings[$field])) {
             $class->reflFields[$field]->setValue($entity, $value);
         }
     }
     // Loading the entity right here, if its in the eager loading map get rid of it there.
     unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
     if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && !$this->eagerLoadingEntities[$class->rootEntityName]) {
         unset($this->eagerLoadingEntities[$class->rootEntityName]);
     }
     // Properly initialize any unfetched associations, if partial objects are not allowed.
     if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
         return $entity;
     }
     foreach ($class->associationMappings as $field => $assoc) {
         // Check if the association is not among the fetch-joined associations already.
         if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
             continue;
         }
         $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
         switch (true) {
             case $assoc['type'] & ClassMetadata::TO_ONE:
                 if (!$assoc['isOwningSide']) {
                     // use the given entity association
                     if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
                         $this->originalEntityData[$oid][$field] = $data[$field];
                         $class->reflFields[$field]->setValue($entity, $data[$field]);
                         $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity);
                         continue 2;
                     }
                     // Inverse side of x-to-one can never be lazy
                     $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity));
                     continue 2;
                 }
                 // use the entity association
                 if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) {
                     $class->reflFields[$field]->setValue($entity, $data[$field]);
                     $this->originalEntityData[$oid][$field] = $data[$field];
                     continue;
                 }
                 $associatedId = array();
                 // TODO: Is this even computed right in all cases of composite keys?
                 foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
                     $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
                     if ($joinColumnValue !== null) {
                         if ($targetClass->containsForeignIdentifier) {
                             $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
                         } else {
                             $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
                         }
                     }
                 }
                 if (!$associatedId) {
                     // Foreign key is NULL
                     $class->reflFields[$field]->setValue($entity, null);
                     $this->originalEntityData[$oid][$field] = null;
                     continue;
                 }
                 if (!isset($hints['fetchMode'][$class->name][$field])) {
                     $hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
                 }
                 // Foreign key is set
                 // Check identity map first
                 // FIXME: Can break easily with composite keys if join column values are in
                 //        wrong order. The correct order is the one in ClassMetadata#identifier.
                 $relatedIdHash = implode(' ', $associatedId);
                 switch (true) {
                     case isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash]):
                         $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
                         // If this is an uninitialized proxy, we are deferring eager loads,
                         // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
                         // then we can append this entity for eager loading!
                         if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && isset($hints[self::HINT_DEFEREAGERLOAD]) && !$targetClass->isIdentifierComposite && $newValue instanceof Proxy && $newValue->__isInitialized__ === false) {
                             $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
                         }
                         break;
                     case $targetClass->subClasses:
                         // If it might be a subtype, it can not be lazy. There isn't even
                         // a way to solve this with deferred eager loading, which means putting
                         // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
                         $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
                         break;
                     default:
                         switch (true) {
                             // We are negating the condition here. Other cases will assume it is valid!
                             case $hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER:
                                 $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
                                 break;
                                 // Deferred eager load only works for single identifier classes
                             // Deferred eager load only works for single identifier classes
                             case isset($hints[self::HINT_DEFEREAGERLOAD]) && !$targetClass->isIdentifierComposite:
                                 // TODO: Is there a faster approach?
                                 $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
                                 $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
                                 break;
                             default:
                                 // TODO: This is very imperformant, ignore it?
                                 $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
                                 break;
                         }
                         // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
                         $newValueOid = spl_object_hash($newValue);
                         $this->entityIdentifiers[$newValueOid] = $associatedId;
                         $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
                         if ($newValue instanceof NotifyPropertyChanged && (!$newValue instanceof Proxy || $newValue->__isInitialized())) {
                             $newValue->addPropertyChangedListener($this);
                         }
                         $this->entityStates[$newValueOid] = self::STATE_MANAGED;
                         // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
                         break;
                 }
                 $this->originalEntityData[$oid][$field] = $newValue;
                 $class->reflFields[$field]->setValue($entity, $newValue);
                 if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
                     $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
                     $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
                 }
                 break;
             default:
                 // Ignore if its a cached collection
                 if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) {
                     break;
                 }
                 // use the given collection
                 if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) {
                     $data[$field]->setOwner($entity, $assoc);
                     $class->reflFields[$field]->setValue($entity, $data[$field]);
                     $this->originalEntityData[$oid][$field] = $data[$field];
                     break;
                 }
                 // Inject collection
                 $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection());
                 $pColl->setOwner($entity, $assoc);
                 $pColl->setInitialized(false);
                 $reflField = $class->reflFields[$field];
                 $reflField->setValue($entity, $pColl);
                 if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
                     $this->loadCollection($pColl);
                     $pColl->takeSnapshot();
                 }
                 $this->originalEntityData[$oid][$field] = $pColl;
                 break;
         }
     }
     if ($overrideLocalValues) {
         $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad);
         if ($invoke !== ListenersInvoker::INVOKE_NONE) {
             $this->listenersInvoker->invoke($class, Events::postLoad, $entity, new LifecycleEventArgs($entity, $this->em), $invoke);
         }
     }
     return $entity;
 }
Beispiel #7
0
 /**
  * INTERNAL:
  * Creates an entity. Used for reconstitution of persistent entities.
  *
  * @ignore
  * @param string $className The name of the entity class.
  * @param array $data The data for the entity.
  * @param array $hints Any hints to account for during reconstitution/lookup of the entity.
  * @return object The managed entity instance.
  * @internal Highly performance-sensitive method.
  * 
  * @todo Rename: getOrCreateEntity
  */
 public function createEntity($className, array $data, &$hints = array())
 {
     $class = $this->em->getClassMetadata($className);
     //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
     if ($class->isIdentifierComposite) {
         $id = array();
         foreach ($class->identifier as $fieldName) {
             $id[$fieldName] = $data[$fieldName];
         }
         $idHash = implode(' ', $id);
     } else {
         $idHash = $data[$class->identifier[0]];
         $id = array($class->identifier[0] => $idHash);
     }
     if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
         $entity = $this->identityMap[$class->rootEntityName][$idHash];
         $oid = spl_object_hash($entity);
         if ($entity instanceof Proxy && !$entity->__isInitialized__) {
             $entity->__isInitialized__ = true;
             $overrideLocalValues = true;
             $this->originalEntityData[$oid] = $data;
             if ($entity instanceof NotifyPropertyChanged) {
                 $entity->addPropertyChangedListener($this);
             }
         } else {
             $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
         }
     } else {
         $entity = $class->newInstance();
         $oid = spl_object_hash($entity);
         $this->entityIdentifiers[$oid] = $id;
         $this->entityStates[$oid] = self::STATE_MANAGED;
         $this->originalEntityData[$oid] = $data;
         $this->identityMap[$class->rootEntityName][$idHash] = $entity;
         if ($entity instanceof NotifyPropertyChanged) {
             $entity->addPropertyChangedListener($this);
         }
         $overrideLocalValues = true;
     }
     if ($overrideLocalValues) {
         foreach ($data as $field => $value) {
             if (isset($class->fieldMappings[$field])) {
                 $class->reflFields[$field]->setValue($entity, $value);
             }
         }
         // Properly initialize any unfetched associations, if partial objects are not allowed.
         if (!isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
             foreach ($class->associationMappings as $field => $assoc) {
                 // Check if the association is not among the fetch-joined associations already.
                 if (isset($hints['fetched'][$className][$field])) {
                     continue;
                 }
                 $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
                 if ($assoc['type'] & ClassMetadata::TO_ONE) {
                     if ($assoc['isOwningSide']) {
                         $associatedId = array();
                         foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
                             $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
                             if ($joinColumnValue !== null) {
                                 $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
                             }
                         }
                         if (!$associatedId) {
                             // Foreign key is NULL
                             $class->reflFields[$field]->setValue($entity, null);
                             $this->originalEntityData[$oid][$field] = null;
                         } else {
                             // Foreign key is set
                             // Check identity map first
                             // FIXME: Can break easily with composite keys if join column values are in
                             //        wrong order. The correct order is the one in ClassMetadata#identifier.
                             $relatedIdHash = implode(' ', $associatedId);
                             if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
                                 $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
                             } else {
                                 if ($targetClass->subClasses) {
                                     // If it might be a subtype, it can not be lazy
                                     $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, null, $associatedId);
                                 } else {
                                     if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
                                         // TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside
                                         // the persister instead of this rather unperformant approach.
                                         $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
                                     } else {
                                         $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
                                     }
                                     // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
                                     $newValueOid = spl_object_hash($newValue);
                                     $this->entityIdentifiers[$newValueOid] = $associatedId;
                                     $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
                                     $this->entityStates[$newValueOid] = self::STATE_MANAGED;
                                     // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
                                 }
                             }
                             $this->originalEntityData[$oid][$field] = $newValue;
                             $class->reflFields[$field]->setValue($entity, $newValue);
                         }
                     } else {
                         // Inverse side of x-to-one can never be lazy
                         $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, null));
                     }
                 } else {
                     // Inject collection
                     $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection());
                     $pColl->setOwner($entity, $assoc);
                     $reflField = $class->reflFields[$field];
                     $reflField->setValue($entity, $pColl);
                     if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) {
                         $pColl->setInitialized(false);
                     } else {
                         $this->loadCollection($pColl);
                         $pColl->takeSnapshot();
                     }
                     $this->originalEntityData[$oid][$field] = $pColl;
                 }
             }
         }
     }
     //TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
     if (isset($class->lifecycleCallbacks[Events::postLoad])) {
         $class->invokeLifecycleCallbacks(Events::postLoad, $entity);
     }
     if ($this->evm->hasListeners(Events::postLoad)) {
         $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
     }
     return $entity;
 }
Beispiel #8
0
 /**
  * @param object $entity
  * @param object $managedCopy
  *
  * @throws ORMException
  * @throws OptimisticLockException
  * @throws TransactionRequiredException
  */
 private function mergeEntityStateIntoManagedCopy($entity, $managedCopy)
 {
     $class = $this->em->getClassMetadata(get_class($entity));
     foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) {
         $name = $prop->name;
         $prop->setAccessible(true);
         if (!isset($class->associationMappings[$name])) {
             if (!$class->isIdentifier($name)) {
                 $prop->setValue($managedCopy, $prop->getValue($entity));
             }
         } else {
             $assoc2 = $class->associationMappings[$name];
             if ($assoc2['type'] & ClassMetadata::TO_ONE) {
                 $other = $prop->getValue($entity);
                 if ($other === null) {
                     $prop->setValue($managedCopy, null);
                 } else {
                     if ($other instanceof Proxy && !$other->__isInitialized()) {
                         // do not merge fields marked lazy that have not been fetched.
                         return;
                     }
                     if (!$assoc2['isCascadeMerge']) {
                         if ($this->getEntityState($other) === self::STATE_DETACHED) {
                             $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
                             $relatedId = $targetClass->getIdentifierValues($other);
                             if ($targetClass->subClasses) {
                                 $other = $this->em->find($targetClass->name, $relatedId);
                             } else {
                                 $other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
                                 $this->registerManaged($other, $relatedId, array());
                             }
                         }
                         $prop->setValue($managedCopy, $other);
                     }
                 }
             } else {
                 $mergeCol = $prop->getValue($entity);
                 if ($mergeCol instanceof PersistentCollection && !$mergeCol->isInitialized()) {
                     // do not merge fields marked lazy that have not been fetched.
                     // keep the lazy persistent collection of the managed copy.
                     return;
                 }
                 $managedCol = $prop->getValue($managedCopy);
                 if (!$managedCol) {
                     $managedCol = new PersistentCollection($this->em, $this->em->getClassMetadata($assoc2['targetEntity']), new ArrayCollection());
                     $managedCol->setOwner($managedCopy, $assoc2);
                     $prop->setValue($managedCopy, $managedCol);
                     $this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol;
                 }
                 if ($assoc2['isCascadeMerge']) {
                     $managedCol->initialize();
                     // clear and set dirty a managed collection if its not also the same collection to merge from.
                     if (!$managedCol->isEmpty() && $managedCol !== $mergeCol) {
                         $managedCol->unwrap()->clear();
                         $managedCol->setDirty(true);
                         if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
                             $this->scheduleForDirtyCheck($managedCopy);
                         }
                     }
                 }
             }
         }
         if ($class->isChangeTrackingNotify()) {
             // Just treat all properties as changed, there is no other choice.
             $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy));
         }
     }
 }
 /**
  * INTERNAL:
  * Creates an entity. Used for reconstitution of persistent entities.
  *
  * @ignore
  * @param string $className The name of the entity class.
  * @param array $data The data for the entity.
  * @param array $hints Any hints to account for during reconstitution/lookup of the entity.
  * @return object The managed entity instance.
  * @internal Highly performance-sensitive method.
  *
  * @todo Rename: getOrCreateEntity
  */
 public function createEntity($className, array $data, &$hints = array())
 {
     $class = $this->em->getClassMetadata($className);
     //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
     if ($class->isIdentifierComposite) {
         $id = array();
         foreach ($class->identifier as $fieldName) {
             if (isset($class->associationMappings[$fieldName])) {
                 $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
             } else {
                 $id[$fieldName] = $data[$fieldName];
             }
         }
         $idHash = implode(' ', $id);
     } else {
         if (isset($class->associationMappings[$class->identifier[0]])) {
             $idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
         } else {
             $idHash = $data[$class->identifier[0]];
         }
         $id = array($class->identifier[0] => $idHash);
     }
     if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
         $entity = $this->identityMap[$class->rootEntityName][$idHash];
         $oid = spl_object_hash($entity);
         if ($entity instanceof Proxy && !$entity->__isInitialized__) {
             $entity->__isInitialized__ = true;
             $overrideLocalValues = true;
             if ($entity instanceof NotifyPropertyChanged) {
                 $entity->addPropertyChangedListener($this);
             }
         } else {
             $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
             // If only a specific entity is set to refresh, check that it's the one
             if (isset($hints[Query::HINT_REFRESH_ENTITY])) {
                 $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity;
             }
         }
         if ($overrideLocalValues) {
             $this->originalEntityData[$oid] = $data;
         }
     } else {
         $entity = $class->newInstance();
         $oid = spl_object_hash($entity);
         $this->entityIdentifiers[$oid] = $id;
         $this->entityStates[$oid] = self::STATE_MANAGED;
         $this->originalEntityData[$oid] = $data;
         $this->identityMap[$class->rootEntityName][$idHash] = $entity;
         if ($entity instanceof NotifyPropertyChanged) {
             $entity->addPropertyChangedListener($this);
         }
         $overrideLocalValues = true;
     }
     if ($overrideLocalValues) {
         foreach ($data as $field => $value) {
             if (isset($class->fieldMappings[$field])) {
                 $class->reflFields[$field]->setValue($entity, $value);
             }
         }
         // Loading the entity right here, if its in the eager loading map get rid of it there.
         unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
         // Properly initialize any unfetched associations, if partial objects are not allowed.
         if (!isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
             foreach ($class->associationMappings as $field => $assoc) {
                 // Check if the association is not among the fetch-joined associations already.
                 if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {
                     continue;
                 }
                 $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
                 if ($assoc['type'] & ClassMetadata::TO_ONE) {
                     if ($assoc['isOwningSide']) {
                         $associatedId = array();
                         // TODO: Is this even computed right in all cases of composite keys?
                         foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
                             $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
                             if ($joinColumnValue !== null) {
                                 if ($targetClass->containsForeignIdentifier) {
                                     $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
                                 } else {
                                     $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
                                 }
                             }
                         }
                         if (!$associatedId) {
                             // Foreign key is NULL
                             $class->reflFields[$field]->setValue($entity, null);
                             $this->originalEntityData[$oid][$field] = null;
                         } else {
                             if (!isset($hints['fetchMode'][$class->name][$field])) {
                                 $hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
                             }
                             // Foreign key is set
                             // Check identity map first
                             // FIXME: Can break easily with composite keys if join column values are in
                             //        wrong order. The correct order is the one in ClassMetadata#identifier.
                             $relatedIdHash = implode(' ', $associatedId);
                             if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
                                 $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
                                 // if this is an uninitialized proxy, we are deferring eager loads,
                                 // this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
                                 // then we cann append this entity for eager loading!
                                 if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite && $newValue instanceof Proxy && $newValue->__isInitialized__ === false) {
                                     $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
                                 }
                             } else {
                                 if ($targetClass->subClasses) {
                                     // If it might be a subtype, it can not be lazy. There isn't even
                                     // a way to solve this with deferred eager loading, which means putting
                                     // an entity with subclasses at a *-to-one location is really bad! (performance-wise)
                                     $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
                                 } else {
                                     // Deferred eager load only works for single identifier classes
                                     if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
                                         if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
                                             // TODO: Is there a faster approach?
                                             $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
                                             $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
                                         } else {
                                             // TODO: This is very imperformant, ignore it?
                                             $newValue = $this->em->find($assoc['targetEntity'], $associatedId);
                                         }
                                     } else {
                                         $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
                                     }
                                     // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
                                     $newValueOid = spl_object_hash($newValue);
                                     $this->entityIdentifiers[$newValueOid] = $associatedId;
                                     $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
                                     $this->entityStates[$newValueOid] = self::STATE_MANAGED;
                                     // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
                                 }
                             }
                             $this->originalEntityData[$oid][$field] = $newValue;
                             $class->reflFields[$field]->setValue($entity, $newValue);
                             if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
                                 $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
                                 $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
                             }
                         }
                     } else {
                         // Inverse side of x-to-one can never be lazy
                         $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity));
                     }
                 } else {
                     // Inject collection
                     $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection());
                     $pColl->setOwner($entity, $assoc);
                     $reflField = $class->reflFields[$field];
                     $reflField->setValue($entity, $pColl);
                     if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
                         $this->loadCollection($pColl);
                         $pColl->takeSnapshot();
                     } else {
                         $pColl->setInitialized(false);
                     }
                     $this->originalEntityData[$oid][$field] = $pColl;
                 }
             }
         }
     }
     //TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
     if (isset($class->lifecycleCallbacks[Events::postLoad])) {
         $class->invokeLifecycleCallbacks(Events::postLoad, $entity);
     }
     if ($this->evm->hasListeners(Events::postLoad)) {
         $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
     }
     return $entity;
 }
Beispiel #10
0
 /**
  * Initializes a related collection.
  *
  * @param object $entity The entity to which the collection belongs.
  * @param string $name The name of the field on the entity that holds the collection.
  */
 private function _initRelatedCollection($entity, $class, $fieldName)
 {
     $oid = spl_object_hash($entity);
     $relation = $class->associationMappings[$fieldName];
     $value = $class->reflFields[$fieldName]->getValue($entity);
     if ($value === null) {
         $value = new ArrayCollection();
     }
     if (!$value instanceof PersistentCollection) {
         $value = new PersistentCollection($this->_em, $this->_ce[$relation->targetEntityName], $value);
         $value->setOwner($entity, $relation);
         $class->reflFields[$fieldName]->setValue($entity, $value);
         $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
         $this->_initializedCollections[$oid . $fieldName] = $value;
     } else {
         if (isset($this->_hints[Query::HINT_REFRESH])) {
             // Is already PersistentCollection, but REFRESH
             $value->setDirty(false);
             $value->setInitialized(true);
             $value->unwrap()->clear();
             $this->_initializedCollections[$oid . $fieldName] = $value;
         } else {
             // Is already PersistentCollection, and DONT REFRESH
             $this->_existingCollections[$oid . $fieldName] = $value;
         }
     }
     return $value;
 }
Beispiel #11
0
 /**
  * Computes the changes done 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.
  */
 private function _computeEntityChanges($class, $entity)
 {
     $oid = spl_object_hash($entity);
     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);
         }
         if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null && !$actualData[$name] instanceof PersistentCollection) {
             //TODO: If $actualData[$name] is Collection then unwrap the array
             $assoc = $class->associationMappings[$name];
             //echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
             // Inject PersistentCollection
             $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $actualData[$name] ? $actualData[$name] : array());
             $coll->setOwner($entity, $assoc);
             if (!$coll->isEmpty()) {
                 $coll->setDirty(true);
             }
             $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() && $assoc->isOwningSide) {
                         $entityIsDirty = true;
                     } 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) {
             if ($entityIsDirty) {
                 $this->_entityUpdates[$oid] = $entity;
             }
             $this->_entityChangeSets[$oid] = $changeSet;
             $this->_originalEntityData[$oid] = $actualData;
         }
     }
 }
 /**
  * Hydrates a single row in an SQL result set.
  * 
  * @internal
  * First, the data of the row is split into chunks where each chunk contains data
  * that belongs to a particular component/class. Afterwards, all these chunks
  * are processed, one after the other. For each chunk of class data only one of the
  * following code paths is executed:
  * 
  * Path A: The data chunk belongs to a joined/associated object and the association
  *         is collection-valued.
  * Path B: The data chunk belongs to a joined/associated object and the association
  *         is single-valued.
  * Path C: The data chunk belongs to a root result element/object that appears in the topmost
  *         level of the hydrated result. A typical example are the objects of the type
  *         specified by the FROM clause in a DQL query. 
  * 
  * @param array $data The data of the row to process.
  * @param array $cache The cache to use.
  * @param array $result The result array to fill.
  */
 protected function _hydrateRow(array $data, array &$cache, array &$result)
 {
     // Initialize
     $id = $this->_idTemplate;
     // initialize the id-memory
     $nonemptyComponents = array();
     // Split the row data into chunks of class data.
     $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
     // Extract scalar values. They're appended at the end.
     if (isset($rowData['scalars'])) {
         $scalars = $rowData['scalars'];
         unset($rowData['scalars']);
         if (empty($rowData)) {
             ++$this->_resultCounter;
         }
     }
     // Hydrate the data chunks
     foreach ($rowData as $dqlAlias => $data) {
         $entityName = $this->_rsm->aliasMap[$dqlAlias];
         if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
             // It's a joined result
             $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
             // we need the $path to save into the identifier map which entities were already
             // seen for this parent-child relationship
             $path = $parentAlias . '.' . $dqlAlias;
             // Get a reference to the parent object to which the joined element belongs.
             if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
                 $first = reset($this->_resultPointers);
                 $parentObject = $this->_resultPointers[$parentAlias][key($first)];
             } else {
                 if (isset($this->_resultPointers[$parentAlias])) {
                     $parentObject = $this->_resultPointers[$parentAlias];
                 } else {
                     // Parent object of relation not found, so skip it.
                     continue;
                 }
             }
             $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
             $oid = spl_object_hash($parentObject);
             $relationField = $this->_rsm->relationMap[$dqlAlias];
             $relation = $parentClass->associationMappings[$relationField];
             $reflField = $parentClass->reflFields[$relationField];
             // Check the type of the relation (many or single-valued)
             if (!($relation['type'] & ClassMetadata::TO_ONE)) {
                 // PATH A: Collection-valued association
                 if (isset($nonemptyComponents[$dqlAlias])) {
                     $collKey = $oid . $relationField;
                     if (isset($this->_initializedCollections[$collKey])) {
                         $reflFieldValue = $this->_initializedCollections[$collKey];
                     } else {
                         if (!isset($this->_existingCollections[$collKey])) {
                             $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
                         }
                     }
                     $indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
                     $index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
                     $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
                     if (!$indexExists || !$indexIsValid) {
                         if (isset($this->_existingCollections[$collKey])) {
                             // Collection exists, only look for the element in the identity map.
                             if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) {
                                 $this->_resultPointers[$dqlAlias] = $element;
                             } else {
                                 unset($this->_resultPointers[$dqlAlias]);
                             }
                         } else {
                             $element = $this->_getEntity($data, $dqlAlias);
                             if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                                 $field = $this->_rsm->indexByMap[$dqlAlias];
                                 $indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
                                 $reflFieldValue->hydrateSet($indexValue, $element);
                                 $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
                             } else {
                                 $reflFieldValue->hydrateAdd($element);
                                 $reflFieldValue->last();
                                 $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
                             }
                             // Update result pointer
                             $this->_resultPointers[$dqlAlias] = $element;
                         }
                     } else {
                         // Update result pointer
                         $this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
                     }
                 } else {
                     if (!$reflField->getValue($parentObject)) {
                         $coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection());
                         $coll->setOwner($parentObject, $relation);
                         $reflField->setValue($parentObject, $coll);
                         $this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
                     }
                 }
             } else {
                 // PATH B: Single-valued association
                 $reflFieldValue = $reflField->getValue($parentObject);
                 if (!$reflFieldValue || isset($this->_hints[Query::HINT_REFRESH])) {
                     if (isset($nonemptyComponents[$dqlAlias])) {
                         $element = $this->_getEntity($data, $dqlAlias);
                         $reflField->setValue($parentObject, $element);
                         $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
                         $targetClass = $this->_ce[$relation['targetEntity']];
                         if ($relation['isOwningSide']) {
                             //TODO: Just check hints['fetched'] here?
                             // If there is an inverse mapping on the target class its bidirectional
                             if ($relation['inversedBy']) {
                                 $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
                                 if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
                                     $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
                                     $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
                                 }
                             } else {
                                 if ($parentClass === $targetClass && $relation['mappedBy']) {
                                     // Special case: bi-directional self-referencing one-one on the same class
                                     $targetClass->reflFields[$relationField]->setValue($element, $parentObject);
                                 }
                             }
                         } else {
                             // For sure bidirectional, as there is no inverse side in unidirectional mappings
                             $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
                             $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
                         }
                         // Update result pointer
                         $this->_resultPointers[$dqlAlias] = $element;
                     }
                     // else leave $reflFieldValue null for single-valued associations
                 } else {
                     // Update result pointer
                     $this->_resultPointers[$dqlAlias] = $reflFieldValue;
                 }
             }
         } else {
             // PATH C: Its a root result element
             $this->_rootAliases[$dqlAlias] = true;
             // Mark as root alias
             if (!isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
                 $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
                 if (isset($this->_rsm->indexByMap[$dqlAlias])) {
                     $field = $this->_rsm->indexByMap[$dqlAlias];
                     $key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
                     if ($this->_rsm->isMixed) {
                         $element = array($key => $element);
                         $result[] = $element;
                         $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
                         ++$this->_resultCounter;
                     } else {
                         $result[$key] = $element;
                         $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
                     }
                     if (isset($this->_hints['collection'])) {
                         $this->_hints['collection']->hydrateSet($key, $element);
                     }
                 } else {
                     if ($this->_rsm->isMixed) {
                         $element = array(0 => $element);
                     }
                     $result[] = $element;
                     $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
                     ++$this->_resultCounter;
                     if (isset($this->_hints['collection'])) {
                         $this->_hints['collection']->hydrateAdd($element);
                     }
                 }
                 // Update result pointer
                 $this->_resultPointers[$dqlAlias] = $element;
             } else {
                 // Update result pointer
                 $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
                 $this->_resultPointers[$dqlAlias] = $result[$index];
                 /*if ($this->_rsm->isMixed) {
                       $result[] = $result[$index];
                       ++$this->_resultCounter;
                   }*/
             }
         }
     }
     // Append scalar values to mixed result sets
     if (isset($scalars)) {
         foreach ($scalars as $name => $value) {
             $result[$this->_resultCounter - 1][$name] = $value;
         }
     }
 }
Beispiel #13
0
 /**
  * INTERNAL:
  * Creates an entity. Used for reconstitution of entities during hydration.
  *
  * @ignore
  * @param string $className The name of the entity class.
  * @param array $data The data for the entity.
  * @param array $hints Any hints to account for during reconstitution/lookup of the entity.
  * @return object The entity instance.
  * @internal Highly performance-sensitive method.
  * 
  * @todo Rename: getOrCreateEntity
  */
 public function createEntity($className, array $data, &$hints = array())
 {
     $class = $this->_em->getClassMetadata($className);
     //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]);
     if ($class->isIdentifierComposite) {
         $id = array();
         foreach ($class->identifier as $fieldName) {
             $id[$fieldName] = $data[$fieldName];
         }
         $idHash = implode(' ', $id);
     } else {
         $idHash = $data[$class->identifier[0]];
         $id = array($class->identifier[0] => $idHash);
     }
     if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
         $entity = $this->_identityMap[$class->rootEntityName][$idHash];
         $oid = spl_object_hash($entity);
         if ($entity instanceof Proxy && !$entity->__isInitialized__) {
             $entity->__isInitialized__ = true;
             $overrideLocalValues = true;
         } else {
             $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
         }
     } else {
         $entity = $class->newInstance();
         $oid = spl_object_hash($entity);
         $this->_entityIdentifiers[$oid] = $id;
         $this->_entityStates[$oid] = self::STATE_MANAGED;
         $this->_originalEntityData[$oid] = $data;
         $this->_identityMap[$class->rootEntityName][$idHash] = $entity;
         if ($entity instanceof NotifyPropertyChanged) {
             $entity->addPropertyChangedListener($this);
         }
         $overrideLocalValues = true;
     }
     if ($overrideLocalValues) {
         if ($this->_useCExtension) {
             doctrine_populate_data($entity, $data);
         } else {
             foreach ($data as $field => $value) {
                 if (isset($class->reflFields[$field])) {
                     $class->reflFields[$field]->setValue($entity, $value);
                 }
             }
         }
         // Properly initialize any unfetched associations, if partial objects are not allowed.
         if (!isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
             foreach ($class->associationMappings as $field => $assoc) {
                 // Check if the association is not among the fetch-joined associations already.
                 if (isset($hints['fetched'][$className][$field])) {
                     continue;
                 }
                 $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
                 if ($assoc->isOneToOne()) {
                     if ($assoc->isOwningSide) {
                         $associatedId = array();
                         foreach ($assoc->targetToSourceKeyColumns as $targetColumn => $srcColumn) {
                             $joinColumnValue = $data[$srcColumn];
                             if ($joinColumnValue !== null) {
                                 $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
                             }
                         }
                         if (!$associatedId) {
                             // Foreign key is NULL
                             $class->reflFields[$field]->setValue($entity, null);
                             $this->_originalEntityData[$oid][$field] = null;
                         } else {
                             // Foreign key is set
                             // Check identity map first
                             // FIXME: Can break easily with composite keys if join column values are in
                             //        wrong order. The correct order is the one in ClassMetadata#identifier.
                             $relatedIdHash = implode(' ', $associatedId);
                             if (isset($this->_identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
                                 $newValue = $this->_identityMap[$targetClass->rootEntityName][$relatedIdHash];
                             } else {
                                 if ($targetClass->subClasses) {
                                     // If it might be a subtype, it can not be lazy
                                     $newValue = $assoc->load($entity, null, $this->_em, $associatedId);
                                 } else {
                                     $newValue = $this->_em->getProxyFactory()->getProxy($assoc->targetEntityName, $associatedId);
                                     $this->_entityIdentifiers[spl_object_hash($newValue)] = $associatedId;
                                     $this->_identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
                                 }
                             }
                             $this->_originalEntityData[$oid][$field] = $newValue;
                             $class->reflFields[$field]->setValue($entity, $newValue);
                         }
                     } else {
                         // Inverse side of x-to-one can never be lazy
                         $class->reflFields[$field]->setValue($entity, $assoc->load($entity, null, $this->_em));
                     }
                 } else {
                     // Inject collection
                     $reflField = $class->reflFields[$field];
                     $pColl = new PersistentCollection($this->_em, $targetClass, $reflField->getValue($entity) ?: new ArrayCollection());
                     $pColl->setOwner($entity, $assoc);
                     $reflField->setValue($entity, $pColl);
                     if ($assoc->isLazilyFetched()) {
                         $pColl->setInitialized(false);
                     } else {
                         $assoc->load($entity, $pColl, $this->_em);
                     }
                     $this->_originalEntityData[$oid][$field] = $pColl;
                 }
             }
         }
     }
     //TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
     if (isset($class->lifecycleCallbacks[Events::postLoad])) {
         $class->invokeLifecycleCallbacks(Events::postLoad, $entity);
     }
     if ($this->_evm->hasListeners(Events::postLoad)) {
         $this->_evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity));
     }
     return $entity;
 }
Beispiel #14
0
 /**
  *
  * @param object $entity
  * @param string $name
  * @todo Consider inlining this method.
  */
 private function initRelatedCollection($entity, $name)
 {
     $oid = spl_object_hash($entity);
     $classMetadata = $this->_ce[get_class($entity)];
     $relation = $classMetadata->associationMappings[$name];
     $relatedClass = $this->_em->getClassMetadata($relation->targetEntityName);
     $coll = new PersistentCollection($this->_em, $relatedClass);
     $this->_collections[] = $coll;
     $coll->setOwner($entity, $relation);
     $classMetadata->reflFields[$name]->setValue($entity, $coll);
     $this->_uow->setOriginalEntityProperty($oid, $name, $coll);
     $this->_initializedRelations[$oid][$name] = true;
     return $coll;
 }