/** * INTERNAL: * Computes the changeset of an individual document, independently of the * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). * * The passed document must be a managed document. If the document already has a change set * because this method is invoked during a commit cycle then the change sets are added. * whereby changes detected in this method prevail. * * @ignore * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to (re)calculate the change set. * @throws InvalidArgumentException If the passed document is not MANAGED. */ public function recomputeSingleDocumentChangeSet($class, $document) { $oid = spl_object_hash($document); if (!isset($this->documentStates[$oid]) || $this->documentStates[$oid] != self::STATE_MANAGED) { throw new \InvalidArgumentException('Document must be managed.'); } if (!$class->isInheritanceTypeNone()) { $class = $this->dm->getClassMetadata(get_class($document)); } $actualData = $this->getDocumentActualData($document); $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $originalData = isset($this->originalDocumentData[$oid]) ? $this->originalDocumentData[$oid] : array(); $changeSet = array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { if ($orgValue !== null) { $this->scheduleOrphanRemoval($orgValue); } $changeSet[$propName] = array($orgValue, $actualValue); } else { if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($isChangeTrackingNotify) { continue; } else { if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') { if ($orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); if ($orgValue instanceof PersistentCollection) { $this->collectionDeletions[] = $orgValue; $this->collectionUpdates[] = $actualValue; $this->visitedCollections[] = $actualValue; } } } else { 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 ($changeSet) { if (isset($this->documentChangeSets[$oid])) { $this->documentChangeSets[$oid] = $changeSet + $this->documentChangeSets[$oid]; } $this->originalDocumentData[$oid] = $actualData; } }
/** * INTERNAL: * Computes the changeset of an individual document, independently of the * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). * * The passed document must be a managed document. If the document already has a change set * because this method is invoked during a commit cycle then the change sets are added. * whereby changes detected in this method prevail. * * @ignore * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to (re)calculate the change set. * @throws \InvalidArgumentException If the passed document is not MANAGED. */ public function recomputeSingleDocumentChangeSet(ClassMetadata $class, $document) { // Ignore uninitialized proxy objects if ($document instanceof Proxy && !$document->__isInitialized__) { return; } $oid = spl_object_hash($document); if (!isset($this->documentStates[$oid]) || $this->documentStates[$oid] != self::STATE_MANAGED) { throw new \InvalidArgumentException('Document must be managed.'); } if (!$class->isInheritanceTypeNone()) { $class = $this->dm->getClassMetadata(get_class($document)); } $this->computeOrRecomputeChangeSet($class, $document, true); }
/** * INTERNAL: * Computes the changeset of an individual document, independently of the * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). * * The passed document must be a managed document. If the document already has a change set * because this method is invoked during a commit cycle then the change sets are added. * whereby changes detected in this method prevail. * * @ignore * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to (re)calculate the change set. * @throws InvalidArgumentException If the passed document is not MANAGED. */ public function recomputeSingleDocumentChangeSet($class, $document) { $oid = spl_object_hash($document); if (!isset($this->_documentStates[$oid]) || $this->_documentStates[$oid] != self::STATE_MANAGED) { throw new \InvalidArgumentException('Document must be managed.'); } if (!$class->isInheritanceTypeNone()) { $class = $this->_dm->getClassMetadata(get_class($document)); } $actualData = array(); foreach ($class->reflFields as $name => $refProp) { if (!$class->isIdentifier($name)) { $actualData[$name] = $refProp->getValue($document); } } $originalData = $this->_originalDocumentData[$oid]; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (is_object($orgValue) && $orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } elseif ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) { $changeSet[$propName] = array($orgValue, $actualValue); } } if ($changeSet) { if (isset($this->_documentChangeSets[$oid])) { $this->_documentChangeSets[$oid] = $changeSet + $this->_documentChangeSets[$oid]; } $this->_originalDocumentData[$oid] = $actualData; } }
/** * 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); } } }
/** * INTERNAL: * Computes the changeset of an individual document, independently of the * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). * * The passed document must be a managed document. If the document already has a change set * because this method is invoked during a commit cycle then the change sets are added. * whereby changes detected in this method prevail. * * @ignore * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to (re)calculate the change set. * @throws InvalidArgumentException If the passed document is not MANAGED. */ public function recomputeSingleDocumentChangeSet($class, $document) { $oid = spl_object_hash($document); if (!isset($this->documentStates[$oid]) || $this->documentStates[$oid] != self::STATE_MANAGED) { throw new \InvalidArgumentException('Document must be managed.'); } if (!$class->isInheritanceTypeNone()) { $class = $this->dm->getClassMetadata(get_class($document)); } $actualData = array(); foreach ($class->reflFields as $name => $refProp) { if (!$class->isIdentifier($name)) { $actualData[$name] = $refProp->getValue($document); } } $originalData = $this->originalDocumentData[$oid]; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if ($orgValue instanceof PersistentCollection) { $orgValue = $orgValue->getSnapshot(); } if ($actualValue instanceof PersistentCollection) { $actualValue = $actualValue->toArray(); } if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one' || isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one') { if ($orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } } else { if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') { $changeSet[$propName] = array($orgValue, $actualValue); } else { 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 ($changeSet) { if (isset($this->documentChangeSets[$oid])) { $this->documentChangeSets[$oid] = $changeSet + $this->documentChangeSets[$oid]; } $this->originalDocumentData[$oid] = $actualData; } }
/** * 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 object $parentDocument The top most parent document of the document we are computing. * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to compute the changes. */ public function computeChangeSet($parentDocument, Mapping\ClassMetadata $class, $document) { if (!$class->isInheritanceTypeNone()) { $class = $this->dm->getClassMetadata(get_class($document)); } $oid = spl_object_hash($document); $parentOid = spl_object_hash($parentDocument); $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $mapping = $class->fieldMappings[$name]; if (!$class->isIdentifier($name) || $class->getAllowCustomID()) { $actualData[$name] = $class->getFieldValue($document, $mapping['fieldName']); } if (($class->isCollectionValuedReference($name) || $class->isCollectionValuedEmbed($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]); } // Inject PersistentCollection if ($class->isCollectionValuedReference($name)) { $coll = new PersistentCollection($actualData[$name], $this->dm); } else { $coll = new PersistentCollection($actualData[$name]); } $coll->setOwner($document, $mapping); $coll->setDirty(!$coll->isEmpty()); $class->reflFields[$name]->setValue($document, $coll); $actualData[$name] = $coll; } elseif ($class->isSingleValuedEmbed($name) && is_object($actualData[$name])) { $embeddedDocument = $actualData[$name]; $embeddedMetadata = $this->dm->getClassMetadata(get_class($embeddedDocument)); $actualData[$name] = array(); foreach ($embeddedMetadata->fieldMappings as $mapping) { $actualData[$name][$mapping['fieldName']] = $embeddedMetadata->getFieldValue($embeddedDocument, $mapping['fieldName']); } $actualData[$name]['originalObject'] = $embeddedDocument; } } 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; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { $changeSet[$propName] = array(null, $actualValue); } $this->documentChangeSets[$oid] = $changeSet; } else { // Document is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->originalDocumentData[$oid]; $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $changeSet = $isChangeTrackingNotify ? $this->documentChangeSets[$oid] : array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if ($orgValue instanceof PersistentCollection) { $orgValue = $orgValue->getSnapshot(); } if ($actualValue instanceof PersistentCollection) { $actualValue = $actualValue->toArray(); } if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { if (is_object($orgValue)) { $embeddedOid = spl_object_hash($orgValue); $orgValue = isset($this->originalDocumentData[$embeddedOid]) ? $this->originalDocumentData[$embeddedOid] : $orgValue; } $changeSet[$propName] = array($orgValue, $actualValue); } else { if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { if (is_object($orgValue)) { $referenceOid = spl_object_hash($orgValue); $orgValue = isset($this->originalDocumentData[$referenceOid]) ? $this->originalDocumentData[$referenceOid] : $orgValue; } $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($isChangeTrackingNotify) { continue; } else { if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') { $changeSet[$propName] = array($orgValue, $actualValue); } else { 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 ($changeSet) { $this->documentChangeSets[$oid] = $changeSet; $this->originalDocumentData[$oid] = $actualData; if (!$class->isEmbeddedDocument) { $this->documentUpdates[$oid] = $document; } } } // Look for changes in references of the document foreach ($class->fieldMappings as $mapping) { if (isset($mapping['reference']) || isset($mapping['embedded'])) { $val = $class->reflFields[$mapping['fieldName']]->getValue($document); if ($val !== null) { $this->computeAssociationChanges($parentDocument, $mapping, $val); } } } }
/** * 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 object $parentDocument The top most parent document of the document we are computing. * @param ClassMetadata $class The class descriptor of the document. * @param object $document The document for which to compute the changes. */ public function computeChangeSet($parentDocument, Mapping\ClassMetadata $class, $document) { if (!$class->isInheritanceTypeNone()) { $class = $this->dm->getClassMetadata(get_class($document)); } $oid = spl_object_hash($document); $actualData = $this->getDocumentActualData($document); 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; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { $changeSet[$propName] = array(null, $actualValue); } $this->documentChangeSets[$oid] = $changeSet; } else { // Document is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->originalDocumentData[$oid]; $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $changeSet = $isChangeTrackingNotify ? $this->documentChangeSets[$oid] : array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if ($orgValue instanceof PersistentCollection) { $orgValue = $orgValue->getSnapshot(); } if ($actualValue instanceof PersistentCollection) { $actualValue = $actualValue->toArray(); } if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { if (is_object($orgValue)) { $embeddedOid = spl_object_hash($orgValue); $orgValue = isset($this->originalDocumentData[$embeddedOid]) ? $this->originalDocumentData[$embeddedOid] : $orgValue; } $changeSet[$propName] = array($orgValue, $actualValue); } else { if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $orgValue !== $actualValue) { if (is_object($orgValue)) { $referenceOid = spl_object_hash($orgValue); $orgValue = isset($this->originalDocumentData[$referenceOid]) ? $this->originalDocumentData[$referenceOid] : $orgValue; } $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($isChangeTrackingNotify) { continue; } else { if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') { $changeSet[$propName] = array($orgValue, $actualValue); } else { 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 ($changeSet) { $this->documentChangeSets[$oid] = $changeSet; $this->originalDocumentData[$oid] = $actualData; if (!$class->isEmbeddedDocument) { $this->documentUpdates[$oid] = $document; } } } // Look for changes in references of the document foreach ($class->fieldMappings as $mapping) { if (isset($mapping['reference']) || isset($mapping['embedded'])) { $val = $class->reflFields[$mapping['fieldName']]->getValue($document); if ($val !== null) { $this->computeAssociationChanges($parentDocument, $mapping, $val); } } } }