/** * 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; }
/** * 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; }
/** * Flush Operation - Write all dirty entries to the CouchDB. * * @return void */ public function flush() { $this->detectChangedDocuments(); if ($this->evm->hasListeners(Event::onFlush)) { $this->evm->dispatchEvent(Event::onFlush, new Event\OnFlushEventArgs($this)); } $config = $this->dm->getConfiguration(); $bulkUpdater = $this->dm->getCouchDBClient()->createBulkUpdater(); $bulkUpdater->setAllOrNothing($config->getAllOrNothingFlush()); foreach ($this->scheduledRemovals as $oid => $document) { $bulkUpdater->deleteDocument($this->documentIdentifiers[$oid], $this->documentRevisions[$oid]); $this->removeFromIdentityMap($document); if ($this->evm->hasListeners(Event::postRemove)) { $this->evm->dispatchEvent(Event::postRemove, new Event\LifecycleEventArgs($document, $this->dm)); } } foreach ($this->scheduledUpdates as $oid => $document) { $class = $this->dm->getClassMetadata(get_class($document)); if ($this->evm->hasListeners(Event::preUpdate)) { $this->evm->dispatchEvent(Event::preUpdate, new Event\LifecycleEventArgs($document, $this->dm)); $this->computeChangeSet($class, $document); // TODO: prevent association computations in this case? } $data = $this->metadataResolver->createDefaultDocumentStruct($class); // Convert field values to json values. foreach ($this->originalData[$oid] as $fieldName => $fieldValue) { if (isset($class->fieldMappings[$fieldName])) { if ($fieldValue !== null && isset($class->fieldMappings[$fieldName]['embedded'])) { // As we store the serialized value in originalEmbeddedData, we can simply copy here. $fieldValue = $this->originalEmbeddedData[$oid][$class->fieldMappings[$fieldName]['jsonName']]; } else { if ($fieldValue !== null) { $fieldValue = Type::getType($class->fieldMappings[$fieldName]['type'])->convertToCouchDBValue($fieldValue); } } $data[$class->fieldMappings[$fieldName]['jsonName']] = $fieldValue; } else { if (isset($class->associationsMappings[$fieldName])) { if ($class->associationsMappings[$fieldName]['type'] & ClassMetadata::TO_ONE) { if (\is_object($fieldValue)) { $fieldValue = $this->getDocumentIdentifier($fieldValue); } else { $fieldValue = null; } $data = $this->metadataResolver->storeAssociationField($data, $class, $this->dm, $fieldName, $fieldValue); } else { if ($class->associationsMappings[$fieldName]['type'] & ClassMetadata::TO_MANY) { if ($class->associationsMappings[$fieldName]['isOwning']) { // TODO: Optimize when not initialized yet! In ManyToMany case we can keep track of ALL ids $ids = array(); if (is_array($fieldValue) || $fieldValue instanceof \Doctrine\Common\Collections\Collection) { foreach ($fieldValue as $key => $relatedObject) { $ids[$key] = $this->getDocumentIdentifier($relatedObject); } } $data = $this->metadataResolver->storeAssociationField($data, $class, $this->dm, $fieldName, $ids); } } } } else { if ($class->hasAttachments && $fieldName == $class->attachmentField) { if (is_array($fieldValue) && $fieldValue) { $data['_attachments'] = array(); foreach ($fieldValue as $filename => $attachment) { if (!$attachment instanceof \Doctrine\CouchDB\Attachment) { throw CouchDBException::invalidAttachment($class->name, $this->documentIdentifiers[$oid], $filename); } $data['_attachments'][$filename] = $attachment->toArray(); } } } } } } // respect the non mapped data, otherwise they will be deleted. if (isset($this->nonMappedData[$oid]) && $this->nonMappedData[$oid]) { $data = array_merge($data, $this->nonMappedData[$oid]); } $rev = $this->getDocumentRevision($document); if ($rev) { $data['_rev'] = $rev; } $bulkUpdater->updateDocument($data); } $response = $bulkUpdater->execute(); $updateConflictDocuments = array(); if ($response->status == 201) { foreach ($response->body as $docResponse) { if (!isset($this->identityMap[$docResponse['id']])) { // deletions continue; } $document = $this->identityMap[$docResponse['id']]; if (isset($docResponse['error'])) { $updateConflictDocuments[] = $document; } else { $this->documentRevisions[spl_object_hash($document)] = $docResponse['rev']; $class = $this->dm->getClassMetadata(get_class($document)); if ($class->isVersioned) { $class->reflFields[$class->versionField]->setValue($document, $docResponse['rev']); } } if ($this->evm->hasListeners(Event::postUpdate)) { $this->evm->dispatchEvent(Event::postUpdate, new Event\LifecycleEventArgs($document, $this->dm)); } } } else { if ($response->status >= 400) { throw HTTPException::fromResponse($bulkUpdater->getPath(), $response); } } foreach ($this->visitedCollections as $col) { $col->takeSnapshot(); } $this->scheduledUpdates = $this->scheduledRemovals = $this->visitedCollections = array(); if (count($updateConflictDocuments)) { throw new UpdateConflictException($updateConflictDocuments); } }
/** * INTERNAL: * Creates an document. Used for reconstitution of documents during hydration. * * @ignore * @param string $className The name of the document class. * @param array $data The data for the document. * @param array $hints Any hints to account for during reconstitution/lookup of the document. * @return object The document instance. * @internal Highly performance-sensitive method. */ public function getOrCreateDocument($className, array $data, &$hints = array()) { $class = $this->_dm->getClassMetadata($className); $id = (string) $data['_id']; if (isset($this->_identityMap[$class->rootDocumentName][$id])) { $document = $this->_identityMap[$class->rootDocumentName][$id]; $oid = spl_object_hash($document); if ($document instanceof Proxy && !$document->__isInitialized__) { $document->__isInitialized__ = true; $overrideLocalValues = true; } else { $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); } } else { $document = $class->newInstance(); $oid = spl_object_hash($document); $this->_documentIdentifiers[$oid] = $id; $this->_documentStates[$oid] = self::STATE_MANAGED; $this->_originalDocumentData[$oid] = $data; $this->_identityMap[$class->rootDocumentName][$id] = $document; $overrideLocalValues = true; } if ($overrideLocalValues) { $this->_hydrator->hydrate($class, $document, $data); } if (isset($class->lifecycleCallbacks[Events::postLoad])) { $class->invokeLifecycleCallbacks(Events::postLoad, $document); } if ($this->_evm->hasListeners(Events::postLoad)) { $this->_evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($document, $this->_dm)); } return $document; }
/** * Clears the UnitOfWork. * * @param string $entityName if given, only entities of this type will get detached */ public function clear($entityName = null) { if ($entityName === null) { $this->identityMap = $this->entityIdentifiers = $this->originalEntityData = $this->entityChangeSets = $this->entityStates = $this->scheduledForDirtyCheck = $this->entityInsertions = $this->entityUpdates = $this->entityDeletions = $this->collectionDeletions = $this->collectionUpdates = $this->extraUpdates = $this->orphanRemovals = array(); if ($this->commitOrderCalculator !== null) { $this->commitOrderCalculator->clear(); } } else { $visited = array(); foreach ($this->identityMap as $className => $entities) { if ($className === $entityName) { foreach ($entities as $entity) { $this->doDetach($entity, $visited, true); } } } } if ($this->evm->hasListeners(Events::onClear)) { $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); } }
/** * @param \Doctrine\OXM\Mapping\ClassMetadata $class * @param $xmlEntity * @return void */ private function persistNew($class, $xmlEntity) { $oid = spl_object_hash($xmlEntity); if (isset($class->lifecycleCallbacks[Events::prePersist])) { $class->invokeLifecycleCallbacks(Events::prePersist, $xmlEntity); } if ($this->evm->hasListeners(Events::prePersist)) { $this->evm->dispatchEvent(Events::prePersist, new Event\LifecycleEventArgs($xmlEntity, $this->xem)); } $idGen = $class->idGenerator; if (!$idGen->isPostInsertGenerator()) { $idValue = $idGen->generate($this->xem, $xmlEntity); if (!$idGen instanceof \Doctrine\OXM\Id\AssignedGenerator) { $this->entityIdentifiers[$oid] = array($class->identifier => $idValue); $class->setIdentifierValue($xmlEntity, $this->entityIdentifiers[$oid]); } else { $this->entityIdentifiers[$oid] = $idValue; } } $this->entityStates[$oid] = self::STATE_MANAGED; $this->scheduleForInsert($xmlEntity); }
/** * 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; }
/** * INTERNAL: * Creates an document. Used for reconstitution of documents during hydration. * * @ignore * @param string $className The name of the document class. * @param array $data The data for the document. * @param array $hints Any hints to account for during reconstitution/lookup of the document. * @return object The document instance. * @internal Highly performance-sensitive method. */ public function getOrCreateDocument($className, $data, &$hints = array()) { $class = $this->dm->getClassMetadata($className); if ($data instanceof \MongoGridFSFile) { $file = $data; $data = $file->file; $data[$class->file] = $file; } if ($class->hasDiscriminator()) { if (isset($data[$class->discriminatorField['name']])) { $type = $data[$class->discriminatorField['name']]; $class = $this->dm->getClassMetadata($class->discriminatorMap[$data[$class->discriminatorField['name']]]); unset($data[$class->discriminatorField['name']]); } } $id = $class->getPHPIdentifierValue($data['_id']); if (isset($this->identityMap[$class->rootDocumentName][$id])) { $document = $this->identityMap[$class->rootDocumentName][$id]; } else { $document = $class->newInstance(); } if (isset($class->lifecycleCallbacks[ODMEvents::preLoad])) { $args = array(&$data); $class->invokeLifecycleCallbacks(ODMEvents::preLoad, $document, $args); } if ($this->evm->hasListeners(ODMEvents::preLoad)) { $this->evm->dispatchEvent(ODMEvents::preLoad, new PreLoadEventArgs($document, $this->dm, $data)); } if (isset($this->identityMap[$class->rootDocumentName][$id])) { $oid = spl_object_hash($document); if ($document instanceof Proxy && !$document->__isInitialized__) { $document->__isInitialized__ = true; $overrideLocalValues = true; if ($document instanceof NotifyPropertyChanged) { $document->addPropertyChangedListener($this); } } else { $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); } if ($overrideLocalValues) { $this->hydrator->hydrate($document, $data); $this->originalDocumentData[$oid] = $data; } } else { $this->hydrator->hydrate($document, $data); $this->registerManaged($document, $id, $data); } if (isset($class->lifecycleCallbacks[ODMEvents::postLoad])) { $class->invokeLifecycleCallbacks(ODMEvents::postLoad, $document); } if ($this->evm->hasListeners(ODMEvents::postLoad)) { $this->evm->dispatchEvent(ODMEvents::postLoad, new LifecycleEventArgs($document, $this->dm)); } return $document; }
/** * Clears the UnitOfWork. */ public function clear() { $this->identityMap = $this->documentIds = $this->documentState = $this->documentTranslations = $this->documentLocales = $this->nonMappedData = $this->originalData = $this->documentChangesets = $this->scheduledUpdates = $this->scheduledAssociationUpdates = $this->scheduledInserts = $this->scheduledMoves = $this->scheduledRemovals = $this->visitedCollections = $this->documentHistory = $this->documentVersion = array(); if ($this->evm->hasListeners(Event::onClear)) { $this->evm->dispatchEvent(Event::onClear, new OnClearEventArgs($this->dm)); } return $this->session->refresh(false); }