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