Gets the changeset for a document.
public getDocumentChangeSet ( object $document ) : array | ||
$document | object | |
Результат | array | array('property' => array(0 => mixed|null, 1 => mixed|null)) |
/** * Prepares the update query to upsert a given document object in mongodb. * * @param object $document * @return array $updateData */ public function prepareUpsertData($document) { $class = $this->dm->getClassMetadata(get_class($document)); $changeset = $this->uow->getDocumentChangeSet($document); $updateData = array(); foreach ($changeset as $fieldName => $change) { $mapping = $class->fieldMappings[$fieldName]; /* Do nothing for embed-many or reference-many collections. If it * uses an atomic strategy, the data will be included when preparing * the atomic query in DocumentPersister::executeUpserts(). Other * strategies will be handled by CollectionPersister. */ if ($mapping['type'] === ClassMetadata::MANY) { continue; } list($old, $new) = $change; // @Inc if ($mapping['type'] === 'increment') { if ($new >= $old) { $updateData['$inc'][$mapping['name']] = $new - $old; } else { $updateData['$inc'][$mapping['name']] = ($old - $new) * -1; } // @Field, @String, @Date, etc. } elseif (!isset($mapping['association'])) { if (isset($new) || $mapping['nullable'] === true) { $updateData['$set'][$mapping['name']] = is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new); } // @EmbedOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { // If we have a new embedded document then lets set the whole thing if ($new && $this->uow->isScheduledForInsert($new)) { $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new); // If we don't have a new value then do nothing on upsert } elseif (!$new) { // Update existing embedded document } else { $update = $this->prepareUpsertData($new); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $updateData[$cmd][$mapping['name'] . '.' . $key] = $value; } } } // @ReferenceOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { if (isset($new) || $mapping['nullable'] === true) { $updateData['$set'][$mapping['name']] = is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new); } } // @EmbedMany and @ReferenceMany are handled by CollectionPersister } // add discriminator if the class has one if (isset($class->discriminatorField)) { $updateData['$set'][$class->discriminatorField] = isset($class->discriminatorValue) ? $class->discriminatorValue : $class->name; } return $updateData; }
/** * Prepares the update query to upsert a given document object in mongodb. * * @param object $document * @return array $updateData */ public function prepareUpsertData($document) { $class = $this->dm->getClassMetadata(get_class($document)); $changeset = $this->uow->getDocumentChangeSet($document); $updateData = array(); foreach ($changeset as $fieldName => $change) { $mapping = $class->fieldMappings[$fieldName]; list($old, $new) = $change; // @Inc if ($mapping['type'] === 'increment') { if ($new >= $old) { $updateData['$inc'][$mapping['name']] = $new - $old; } else { $updateData['$inc'][$mapping['name']] = ($old - $new) * -1; } // @Field, @String, @Date, etc. } elseif (!isset($mapping['association'])) { if (isset($new) || $mapping['nullable'] === true) { $updateData['$set'][$mapping['name']] = is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new); } // @EmbedOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { // If we have a new embedded document then lets set the whole thing if ($new && $this->uow->isScheduledForInsert($new)) { $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new); // If we don't have a new value then do nothing on upsert } elseif (!$new) { // Update existing embedded document } else { $update = $this->prepareUpsertData($new); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $updateData[$cmd][$mapping['name'] . '.' . $key] = $value; } } } // @ReferenceOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { if (isset($new) || $mapping['nullable'] === true) { $updateData['$set'][$mapping['name']] = is_null($new) ? null : $this->prepareReferencedDocumentValue($mapping, $new); } // @ReferenceMany, @EmbedMany } elseif ($mapping['type'] === ClassMetadata::MANY && !$mapping['isInverseSide'] && $new instanceof PersistentCollection && $new->isDirty() && CollectionHelper::isAtomic($mapping['strategy'])) { $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true); } // @EmbedMany and @ReferenceMany are handled by CollectionPersister } // add discriminator if the class has one if (isset($class->discriminatorField)) { $updateData['$set'][$class->discriminatorField] = isset($class->discriminatorValue) ? $class->discriminatorValue : $class->name; } return $updateData; }
/** * If the document is new, ignore shard key field value, otherwise throw an exception. * Also, shard key field should be present in actual document data. * * @param object $document * @param string $shardKeyField * @param array $actualDocumentData * * @throws MongoDBException */ private function guardMissingShardKey($document, $shardKeyField, $actualDocumentData) { $dcs = $this->uow->getDocumentChangeSet($document); $isUpdate = $this->uow->isScheduledForUpdate($document); $fieldMapping = $this->class->getFieldMappingByDbFieldName($shardKeyField); $fieldName = $fieldMapping['fieldName']; if ($isUpdate && isset($dcs[$fieldName]) && $dcs[$fieldName][0] != $dcs[$fieldName][1]) { throw MongoDBException::shardKeyFieldCannotBeChanged($shardKeyField, $this->class->getName()); } if (!isset($actualDocumentData[$fieldName])) { throw MongoDBException::shardKeyFieldMissing($shardKeyField, $this->class->getName()); } }
/** * Cascades the postUpdate and postPersist events to embedded documents. * * @param ClassMetadata $class * @param object $document */ private function cascadePostUpdate(ClassMetadata $class, $document) { foreach ($class->getEmbeddedFieldsMappings() as $mapping) { $value = $class->reflFields[$mapping['fieldName']]->getValue($document); if ($value === null) { continue; } $values = $mapping['type'] === ClassMetadata::ONE ? array($value) : $value; foreach ($values as $entry) { if (empty($this->uow->getDocumentChangeSet($entry)) && !$this->uow->hasScheduledCollections($entry)) { continue; } $entryClass = $this->dm->getClassMetadata(get_class($entry)); $event = $this->uow->isScheduledForInsert($entry) ? Events::postPersist : Events::postUpdate; $entryClass->invokeLifecycleCallbacks($event, $entry, array(new LifecycleEventArgs($entry, $this->dm))); $this->evm->dispatchEvent($event, new LifecycleEventArgs($entry, $this->dm)); $this->cascadePostUpdate($entryClass, $entry); } } }
/** * @param $document * @return array * @throws MongoDBException */ public function getShardKeyQuery($document) { $shardKeysQueryPart = array(); $dcs = $this->uow->getDocumentChangeSet($document); $data = $this->uow->getDocumentActualData($document); $md = $this->dm->getMetadataFactory()->getMetadataFor(get_class($document)); $keys = $md->shardKeys; $fieldMappings = $this->dm->getClassMetadata(get_class($document))->fieldMappings; foreach ($keys as $key) { if ($key !== 'id') { $queryKey = $fieldMappings[$key]['name']; } else { $queryKey = '_id'; } //If the document is new, we can ignore shard key value, otherwise throw exception $isUpdate = $this->uow->isScheduledForUpdate($document); if ($isUpdate && isset($dcs[$key]) && $dcs[$key][0] !== null && $dcs[$key][0] != $dcs[$key][1] && (!isset($options['upsert']) || isset($options['upsert']) && $options['upsert'] === true)) { throw MongoDBException::shardKeyChange($key); } if (!isset($data[$key])) { throw MongoDBException::shardKeyMissing($key); } $new = $data[$key]; $mapping = $fieldMappings[$key]; // @Field, @String, @Date, etc. if (!isset($mapping['association'])) { if (isset($new) || $mapping['nullable'] === true) { $shardKeysQueryPart[$queryKey] = is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new); } // @ReferenceOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { if (isset($new) || $mapping['nullable'] === true) { $shardKeysQueryPart[$queryKey] = is_null($new) ? null : $this->pb->prepareReferencedDocumentValue($mapping, $new); } // @ReferenceMany } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { // Do nothing right now } } return $shardKeysQueryPart; }
/** * Prepares update array for document, using atomic operators * * @param mixed $document * @return array */ public function prepareUpdateData($document) { $oid = spl_object_hash($document); $changeset = $this->_uow->getDocumentChangeSet($document); $result = array(); foreach ($this->_class->fieldMappings as $mapping) { if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { continue; } $old = isset($changeset[$mapping['fieldName']][0]) ? $changeset[$mapping['fieldName']][0] : null; $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null; $new = $this->_prepareValue($mapping, $new); $old = $this->_prepareValue($mapping, $old); if ($this->_class->isIdentifier($mapping['fieldName']) && !$this->_equals($new, $old)) { throw MongoDBException::identifierCannotBeUpdated(); } if ($mapping['type'] === 'many' || $mapping['type'] === 'collection') { $this->_addArrayUpdateAtomicOperator($mapping, (array) $new, (array) $old, $result); } else { $this->_addFieldUpdateAtomicOperator($mapping, $new, $old, $result); } } return $result; }
/** * Prepares the update query to update a given document object in mongodb. * * @param object $document * @return array $updateData */ public function prepareUpdateData($document) { $oid = spl_object_hash($document); $class = $this->dm->getClassMetadata(get_class($document)); $changeset = $this->uow->getDocumentChangeSet($document); $updateData = array(); foreach ($changeset as $fieldName => $change) { $mapping = $class->fieldMappings[$fieldName]; // skip not saved fields if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { continue; } // Skip version and lock fields if (isset($mapping['version']) || isset($mapping['lock'])) { continue; } list($old, $new) = $change; // @Inc if ($mapping['type'] === 'increment') { if ($new >= $old) { $updateData[$this->cmd . 'inc'][$mapping['name']] = $new - $old; } else { $updateData[$this->cmd . 'inc'][$mapping['name']] = ($old - $new) * -1; } // @Field, @String, @Date, etc. } elseif (!isset($mapping['association'])) { if (isset($new) || $mapping['nullable'] === true) { $updateData[$this->cmd . 'set'][$mapping['name']] = Type::getType($mapping['type'])->convertToDatabaseValue($new); } else { $updateData[$this->cmd . 'unset'][$mapping['name']] = true; } // @EmbedOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) { // If we have a new embedded document then lets set the whole thing if ($new && $this->uow->isScheduledForInsert($new)) { $updateData[$this->cmd . 'set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new); // If we don't have a new value then lets unset the embedded document } elseif (!$new) { $updateData[$this->cmd . 'unset'][$mapping['name']] = true; // Update existing embedded document } else { $update = $this->prepareUpdateData($new); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $updateData[$cmd][$mapping['name'] . '.' . $key] = $value; } } } // @EmbedMany } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_MANY) { foreach ($new as $key => $embeddedDoc) { if (!$this->uow->isScheduledForInsert($embeddedDoc)) { $update = $this->prepareUpdateData($embeddedDoc); foreach ($update as $cmd => $values) { foreach ($values as $name => $value) { $updateData[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value; } } } } // @ReferenceOne } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { if (isset($new) || $mapping['nullable'] === true) { $updateData[$this->cmd . 'set'][$mapping['name']] = $this->prepareReferencedDocumentValue($mapping, $new); } else { $updateData[$this->cmd . 'unset'][$mapping['name']] = true; } // @ReferenceMany } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) { // Do nothing right now } } return $updateData; }
/** * Prepares update array for document, using atomic operators * * @param mixed $document * @return array */ public function prepareUpdateData($document) { if (is_array($document) && isset($document['originalObject'])) { $document = $document['originalObject']; } $oid = spl_object_hash($document); $class = $this->dm->getClassMetadata(get_class($document)); $changeset = $this->uow->getDocumentChangeSet($document); $result = array(); foreach ($class->fieldMappings as $mapping) { if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { continue; } $old = isset($changeset[$mapping['fieldName']][0]) ? $changeset[$mapping['fieldName']][0] : null; $new = isset($changeset[$mapping['fieldName']][1]) ? $changeset[$mapping['fieldName']][1] : null; if ($mapping['type'] === 'many' || $mapping['type'] === 'collection') { if ($mapping['strategy'] === 'pushPull') { if (isset($mapping['embedded']) && $new) { foreach ($new as $k => $v) { if (!isset($old[$k])) { continue; } $update = $this->prepareUpdateData($v); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $result[$cmd][$mapping['name'] . '.' . $k . '.' . $key] = $value; } } } } if ($old !== $new) { $old = $old ? $old : array(); $new = $new ? $new : array(); $compare = function ($a, $b) { $a = is_array($a) && isset($a['originalObject']) ? $a['originalObject'] : $a; $b = is_array($b) && isset($b['originalObject']) ? $b['originalObject'] : $b; return $a === $b ? 0 : 1; }; $deleteDiff = array_udiff_assoc($old, $new, $compare); $insertDiff = array_udiff_assoc($new, $old, $compare); // insert diff if ($insertDiff) { $result[$this->cmd . 'pushAll'][$mapping['name']] = $this->prepareValue($mapping, $insertDiff); } // delete diff if ($deleteDiff) { $result[$this->cmd . 'pullAll'][$mapping['name']] = $this->prepareValue($mapping, $deleteDiff); } } } elseif ($mapping['strategy'] === 'set') { if ($old !== $new) { $new = $this->prepareValue($mapping, $new); $result[$this->cmd . 'set'][$mapping['name']] = $new; } } } else { if ($old !== $new) { if ($mapping['type'] === 'increment') { $new = $this->prepareValue($mapping, $new); $old = $this->prepareValue($mapping, $old); if ($new >= $old) { $result[$this->cmd . 'inc'][$mapping['name']] = $new - $old; } else { $result[$this->cmd . 'inc'][$mapping['name']] = ($old - $new) * -1; } } else { // Single embedded if (isset($mapping['embedded']) && $mapping['type'] === 'one') { // If we didn't have a value before and now we do if (!$old && $new) { $new = $this->prepareValue($mapping, $new); if (isset($new) || $mapping['nullable'] === true) { $result[$this->cmd . 'set'][$mapping['name']] = $new; } // If we had an old value before and it has changed } elseif ($old && $new) { $embeddedDocument = $class->getFieldValue($document, $mapping['fieldName']); $update = $this->prepareUpdateData($embeddedDocument); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $result[$cmd][$mapping['name'] . '.' . $key] = $value; } } } // $set all other fields } else { $new = $this->prepareValue($mapping, $new); if (isset($new) || $mapping['nullable'] === true) { $result[$this->cmd . 'set'][$mapping['name']] = $new; } else { $result[$this->cmd . 'unset'][$mapping['name']] = true; } } } } } } return $result; }
/** * @param <type> $document * @override */ public function getDocumentChangeSet($document) { $oid = spl_object_hash($document); return isset($this->_mockDataChangeSets[$oid]) ? $this->_mockDataChangeSets[$oid] : parent::getDocumentChangeSet($document); }
/** * Get the object changeset from a UnitOfWork * * @param UnitOfWork $uow * @param Object $object * @return array */ public function getObjectChangeSet(UnitOfWork $uow, $object) { return $uow->getDocumentChangeSet($object); }
/** * Prepares update array for document, using atomic operators * * @param mixed $document * @return array */ public function prepareUpdateData($document) { $oid = spl_object_hash($document); $class = $this->dm->getClassMetadata(get_class($document)); $changeset = $this->uow->getDocumentChangeSet($document); $result = array(); foreach ($changeset as $fieldName => $change) { $mapping = $class->fieldMappings[$fieldName]; // many references are persisted with CollectionPersister later if (isset($mapping['reference']) && $mapping['type'] === 'many') { continue; } // skip not saved fields if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) { continue; } // Skip version and lock fields if (isset($mapping['version']) || isset($mapping['lock'])) { continue; } list($old, $new) = $change; // Build query to persist updates to an embedded one association if (isset($mapping['embedded']) && $mapping['type'] === 'one') { // If we have a new embedded document then lets set the whole thing if ($new && $this->uow->isScheduledForInsert($new)) { $result[$this->cmd . 'set'][$mapping['name']] = $this->prepareEmbeddedDocValue($mapping, $new); // If we don't have a new value then lets unset the embedded document } else if ( ! $new) { $result[$this->cmd . 'unset'][$mapping['name']] = true; // Update existing embedded document } else { $update = $this->prepareUpdateData($new); foreach ($update as $cmd => $values) { foreach ($values as $key => $value) { $result[$cmd][$mapping['name'] . '.' . $key] = $value; } } } // Build query to persist updates to an embedded many association } else if (isset($mapping['embedded']) && $mapping['type'] === 'many') { foreach ($new as $key => $embeddedDoc) { if (!$this->uow->isScheduledForInsert($embeddedDoc)) { $update = $this->prepareUpdateData($embeddedDoc); foreach ($update as $cmd => $values) { foreach ($values as $name => $value) { $result[$cmd][$mapping['name'] . '.' . $key . '.' . $name] = $value; } } } } // Prepare increment type values } else if ($mapping['type'] === 'increment') { if ($new >= $old) { $result[$this->cmd . 'inc'][$mapping['name']] = $new - $old; } else { $result[$this->cmd . 'inc'][$mapping['name']] = ($old - $new) * -1; } // Persist all other types using $set and $unset } else { if (isset($new) || $mapping['nullable'] === true) { $new = $this->prepareValue($mapping, $new); $result[$this->cmd . 'set'][$mapping['name']] = $new; } else { $result[$this->cmd . 'unset'][$mapping['name']] = true; } } } return $result; }