/**
* Computes the changes that happened to a single document.
*
* Modifies/populates the following properties:
*
* {@link _originalDocumentData}
* If the document 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
* document data yet. All of the current document data is stored as the original document data.
*
* {@link _documentChangeSets}
* The changes detected on all properties of the document 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 document state.
*
* {@link _documentUpdates}
* If the document is already fully MANAGED (has been fetched from the database before)
* and any changes to its properties are detected, then a reference to the document is stored
* there to mark it for an update.
*
* @param ClassMetadata $class The class descriptor of the document.
* @param object $document The document for which to compute the changes.
*/
public function computeChangeSet(Mapping\ClassMetadata $class, $document)
{
if (!$class->isInheritanceTypeNone()) {
$class = $this->_dm->getClassMetadata(get_class($document));
}
$oid = spl_object_hash($document);
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
if (!$class->isIdentifier($name)) {
$actualData[$name] = $refProp->getValue($document);
}
if ($class->isCollectionValuedReference($name) && $actualData[$name] !== null && !$actualData[$name] instanceof PersistentCollection) {
// If $actualData[$name] is not a Collection then use an ArrayCollection.
if (!$actualData[$name] instanceof Collection) {
$actualData[$name] = new ArrayCollection($actualData[$name]);
}
$mapping = $class->fieldMappings[$name];
// Inject PersistentCollection
$coll = new PersistentCollection($this->_dm, $this->_dm->getClassMetadata($mapping['targetDocument']), $actualData[$name]);
$coll->setOwner($document, $mapping);
$coll->setDirty(!$coll->isEmpty());
$class->reflFields[$name]->setValue($document, $coll);
$actualData[$name] = $coll;
}
}
if (!isset($this->_originalDocumentData[$oid])) {
// Document is either NEW or MANAGED but not yet fully persisted (only has an id).
// These result in an INSERT.
$this->_originalDocumentData[$oid] = $actualData;
$this->_documentChangeSets[$oid] = array_map(function ($e) {
return array(null, $e);
}, $actualData);
} else {
// Document is "fully" MANAGED: it was already fully persisted before
// and we have a copy of the original data
$originalData = $this->_originalDocumentData[$oid];
$changeSet = array();
$documentIsDirty = false;
foreach ($actualData as $propName => $actualValue) {
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
if (is_object($orgValue)) {
if ($orgValue instanceof PersistentCollection) {
$orgValue = $orgValue->getSnapshot();
}
if ($actualValue instanceof PersistentCollection) {
$actualValue = $actualValue->toArray();
}
if ($orgValue !== $actualValue) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
} elseif ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
if (isset($changeSet[$propName])) {
if (isset($class->fieldMappings[$propName]['reference'])) {
$mapping = $class->fieldMappings[$propName];
if ($mapping['type'] === 'one') {
$documentIsDirty = true;
if ($actualValue === null) {
$this->scheduleOrphanRemoval($orgValue);
}
}
} else {
$documentIsDirty = true;
}
}
}
if ($changeSet) {
$this->_documentChangeSets[$oid] = $changeSet;
$this->_originalDocumentData[$oid] = $actualData;
if ($documentIsDirty) {
$this->_documentUpdates[$oid] = $document;
}
}
}
// Look for changes in references of the document
foreach ($class->fieldMappings as $mapping) {
if (!isset($mapping['reference'])) {
continue;
}
$val = $class->reflFields[$mapping['fieldName']]->getValue($document);
if ($val !== null) {
$this->_computeAssociationChanges($mapping, $val);
}
}
}