/** * Gets the specified field's value off the given document. * * @param object $document * @param string $field */ public function getFieldValue($document, $field) { if ($document instanceof Proxy && !$document->__isInitialized()) { if ($field === $this->identifier) { return $document->__identifier__; } else { $document->__load(); } } return $this->reflFields[$field]->getValue($document); }
/** * @param object $entity * @return array * @throws NotManageableEntityException */ public function getEntityIdentifier($entity) { if ($entity instanceof Proxy && !$entity->__isInitialized()) { $identifierProperty = new \ReflectionProperty(get_class($entity), '_identifier'); $identifierProperty->setAccessible(true); $identifier = $identifierProperty->getValue($entity); } else { $entityManager = $this->getEntityManager($entity); $metadata = $entityManager->getClassMetadata(get_class($entity)); $identifier = $metadata->getIdentifierValues($entity); } return $identifier; }
/** * @param object $entity * @return object|null */ protected function refreshEntity($entity) { if ($entity instanceof Proxy && !$entity->__isInitialized()) { $entity->__load(); } if (!$this->doctrineHelper->getSingleEntityIdentifier($entity)) { return null; } $entityClass = ClassUtils::getClass($entity); /** @var EntityManager $entityManager */ $entityManager = $this->registry->getManagerForClass($entityClass); return $entityManager->merge($entity); }
/** * Computes changeset for a given document. * * @param ClassMetadata $class * @param object $document */ public function computeChangeSet(ClassMetadata $class, $document) { if ($document instanceof Proxy && !$document->__isInitialized()) { return; } $oid = spl_object_hash($document); if (in_array($oid, $this->changesetComputed)) { return; } $this->changesetComputed[] = $oid; $changeSet = $actualData = $this->getDocumentActualData($class, $document); $id = $this->getDocumentId($document, false); $isNew = !isset($this->originalData[$oid]); if ($isNew) { // Document is New and should be inserted $this->originalData[$oid] = $changeSet; } elseif (!empty($this->documentChangesets[$oid]['fields'])) { foreach ($this->documentChangesets[$oid]['fields'] as $fieldName => $data) { $this->originalData[$oid][$fieldName] = $data[0]; } } if ($class->parentMapping && isset($changeSet[$class->parentMapping])) { $parent = $changeSet[$class->parentMapping]; $parentClass = $this->dm->getClassMetadata(get_class($parent)); $state = $this->getDocumentState($parent); if ($state === self::STATE_MANAGED) { $this->computeChangeSet($parentClass, $parent); } } foreach ($class->childMappings as $fieldName) { if ($changeSet[$fieldName]) { if (is_array($changeSet[$fieldName]) || $changeSet[$fieldName] instanceof Collection) { throw PHPCRException::childFieldIsArray(self::objToStr($document, $this->dm), $fieldName); } if (!is_object($changeSet[$fieldName])) { throw PHPCRException::childFieldNoObject(self::objToStr($document, $this->dm), $fieldName, gettype($changeSet[$fieldName])); } $mapping = $class->mappings[$fieldName]; $changeSet[$fieldName] = $this->computeChildChanges($mapping, $changeSet[$fieldName], $id, $mapping['nodeName']); } } $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'reference'); $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'referrer'); foreach ($class->mixedReferrersMappings as $fieldName) { if ($changeSet[$fieldName] && $changeSet[$fieldName] instanceof PersistentCollection && $changeSet[$fieldName]->isDirty()) { throw new PHPCRException("The immutable mixed referrer collection in field {$fieldName} is dirty"); } } $this->computeChildrenChanges($document, $class, $oid, $isNew, $changeSet); if (!$isNew) { // collect assignment move operations $destPath = $destName = false; if (isset($this->originalData[$oid][$class->parentMapping]) && isset($changeSet[$class->parentMapping]) && $this->originalData[$oid][$class->parentMapping] !== $changeSet[$class->parentMapping]) { $destPath = $this->getDocumentId($changeSet[$class->parentMapping]); } if (isset($this->originalData[$oid][$class->nodename]) && isset($changeSet[$class->nodename]) && $this->originalData[$oid][$class->nodename] !== $changeSet[$class->nodename]) { $destName = $changeSet[$class->nodename]; } // there was assignment move if ($destPath || $destName) { // add the other field if only one was changed if (false === $destPath) { $destPath = isset($changeSet[$class->parentMapping]) ? $this->getDocumentId($changeSet[$class->parentMapping]) : PathHelper::getParentPath($this->getDocumentId($document)); } if (false === $destName) { $destName = $class->nodename !== null && $changeSet[$class->nodename] ? $changeSet[$class->nodename] : PathHelper::getNodeName($this->getDocumentId($document)); } // make sure destination nodename is okay if ($exception = $class->isValidNodename($destName)) { throw IdException::illegalName($document, $class->nodename, $destName); } // prevent path from becoming "//foobar" when moving to root node. $targetPath = '/' == $destPath ? "/{$destName}" : "{$destPath}/{$destName}"; $this->scheduleMove($document, $targetPath); } if (isset($this->originalData[$oid][$class->identifier]) && isset($changeSet[$class->identifier]) && $this->originalData[$oid][$class->identifier] !== $changeSet[$class->identifier]) { throw new PHPCRException('The Id is immutable (' . $this->originalData[$oid][$class->identifier] . ' !== ' . $changeSet[$class->identifier] . '). Please use DocumentManager::move to move the document: ' . self::objToStr($document, $this->dm)); } } $fields = array_intersect_key($changeSet, $class->mappings); if ($this->isDocumentTranslatable($class)) { $locale = $this->getCurrentLocale($document, $class); // ensure we do not bind a previously removed translation if (!$this->isTranslationRemoved($document, $locale)) { $this->doBindTranslation($document, $locale, $class); } } if ($isNew) { $this->documentChangesets[$oid]['fields'] = $fields; $this->scheduledInserts[$oid] = $document; return; } $translationChanges = false; if ($this->isDocumentTranslatable($class)) { $oid = spl_object_hash($document); if (isset($this->documentTranslations[$oid])) { foreach ($this->documentTranslations[$oid] as $localeToCheck => $data) { // a translation was removed if (empty($data)) { $translationChanges = true; break; } // a translation was added if (empty($this->originalTranslatedData[$oid][$localeToCheck])) { $translationChanges = true; break; } // a translation was changed foreach ($data as $fieldName => $fieldValue) { if ($this->originalTranslatedData[$oid][$localeToCheck][$fieldName] !== $fieldValue) { $translationChanges = true; break; } } } } // ensure that locale changes are not considered a change in the document if ($class->localeMapping && array_key_exists($class->localeMapping, $fields)) { unset($fields[$class->localeMapping]); } } foreach ($fields as $fieldName => $fieldValue) { $keepChange = false; if ($fieldValue instanceof ReferenceManyCollection || $fieldValue instanceof ReferrersCollection) { if ($fieldValue->changed()) { $keepChange = true; } } elseif ($this->originalData[$oid][$fieldName] !== $fieldValue) { $keepChange = true; } if ($keepChange) { $fields[$fieldName] = array($this->originalData[$oid][$fieldName], $fieldValue); } else { unset($fields[$fieldName]); } } if (!empty($fields) || $translationChanges) { $this->documentChangesets[$oid]['fields'] = $fields; $this->originalData[$oid] = $actualData; $this->scheduledUpdates[$oid] = $document; } elseif (empty($this->documentChangesets[$oid]['reorderings'])) { unset($this->documentChangesets[$oid]); unset($this->scheduledUpdates[$oid]); } else { $this->documentChangesets[$oid]['fields'] = array(); } }
/** * Tests if an entity is loaded - must either be a loaded proxy or not a proxy * * @param object $entity * * @return bool */ private function isLoaded($entity) { return !$entity instanceof Proxy || $entity->__isInitialized(); }
/** * @param ClassMetadata $class * @param object $document * @return void */ private function computeChangeSet(ClassMetadata $class, $document) { if ($document instanceof Proxy && !$document->__isInitialized()) { return; } $actualData = array(); foreach ($class->reflFields as $fieldName => $reflProperty) { $value = $reflProperty->getValue($document); if ($class->isCollectionValuedAssociation($fieldName) && $value !== null && !$value instanceof PersistentCollection) { if (!$value instanceof Collection) { $value = new MultivaluePropertyCollection(new ArrayCollection($value), true); $this->multivaluePropertyCollections[] = $value; } $collection = $value; $class->reflFields[$fieldName]->setValue($document, $collection); $actualData[$fieldName] = $collection; } else { $actualData[$fieldName] = $value; } } // unset the version info fields if they have values, they are not to be managed by the user in write scenarios. if ($class->versionable) { unset($actualData[$class->versionNameField]); unset($actualData[$class->versionCreatedField]); } $oid = spl_object_hash($document); if (!isset($this->originalData[$oid])) { // Document is New and should be inserted $this->originalData[$oid] = $actualData; $this->documentChangesets[$oid] = $actualData; $this->scheduledInserts[$oid] = $document; } else { if (isset($this->originalData[$oid][$class->nodename]) && isset($actualData[$class->nodename]) && $this->originalData[$oid][$class->nodename] !== $actualData[$class->nodename]) { throw new PHPCRException('The Nodename property is immutable (' . $this->originalData[$oid][$class->nodename] . ' !== ' . $actualData[$class->nodename] . '). Please use DocumentManager::move to rename the document: ' . self::objToStr($document, $this->dm)); } if (isset($this->originalData[$oid][$class->parentMapping]) && isset($actualData[$class->parentMapping]) && $this->originalData[$oid][$class->parentMapping] !== $actualData[$class->parentMapping]) { throw new PHPCRException('The ParentDocument property is immutable (' . $class->getIdentifierValue($this->originalData[$oid][$class->parentMapping]) . ' !== ' . $class->getIdentifierValue($actualData[$class->parentMapping]) . '). Please use PHPCR\\Session::move to move the document: ' . self::objToStr($document, $this->dm)); } if (isset($this->originalData[$oid][$class->identifier]) && isset($actualData[$class->identifier]) && $this->originalData[$oid][$class->identifier] !== $actualData[$class->identifier]) { throw new PHPCRException('The Id is immutable (' . $this->originalData[$oid][$class->identifier] . ' !== ' . $actualData[$class->identifier] . '). Please use DocumentManager::move to move the document: ' . self::objToStr($document, $this->dm)); } // Document is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $changed = false; foreach ($actualData as $fieldName => $fieldValue) { if (!isset($class->fieldMappings[$fieldName]) && !isset($class->childMappings[$fieldName]) && !isset($class->associationsMappings[$fieldName]) && !isset($class->referrersMappings[$fieldName]) && !isset($class->parentMapping[$fieldName]) && !isset($class->nodename)) { continue; } if ($class->isCollectionValuedAssociation($fieldName)) { if (!$fieldValue instanceof PersistentCollection) { // if its not a persistent collection and the original value changed. otherwise it could just be null $changed = true; break; } elseif ($fieldValue->changed()) { $this->visitedCollections[] = $fieldValue; $changed = true; break; } } elseif ($this->originalData[$oid][$fieldName] !== $fieldValue) { $changed = true; break; } elseif ($fieldValue instanceof ReferenceManyCollection) { if ($fieldValue->changed()) { $changed = true; } } } if (isset($this->documentLocales[$oid]) && $this->documentLocales[$oid]['current'] !== $this->documentLocales[$oid]['original']) { $changed = true; } if ($changed) { $this->documentChangesets[$oid] = $actualData; $this->scheduledUpdates[$oid] = $document; } } if ($class->parentMapping && isset($actualData[$class->parentMapping])) { $parent = $actualData[$class->parentMapping]; $parentClass = $this->dm->getClassMetadata(get_class($parent)); $state = $this->getDocumentState($parent); if ($state === self::STATE_MANAGED) { $this->computeChangeSet($parentClass, $parent); } } $id = $class->getIdentifierValue($document); foreach ($class->childMappings as $name => $childMapping) { if ($actualData[$name]) { if ($this->originalData[$oid][$name] && $this->originalData[$oid][$name] !== $actualData[$name]) { throw new PHPCRException('Cannot move/copy children by assignment as it would be ambiguous. Please use the DocumentManager::move() or PHPCR\\Session::copy() operations for this: ' . self::objToStr($document, $this->dm)); } $this->computeChildChanges($childMapping, $actualData[$name], $id); } } foreach ($class->associationsMappings as $assocName => $assoc) { if ($actualData[$assocName]) { if (is_array($actualData[$assocName]) || $actualData[$assocName] instanceof Collection) { foreach ($actualData[$assocName] as $ref) { if ($ref !== null) { $this->computeReferenceChanges($ref); } } } else { $this->computeReferenceChanges($actualData[$assocName]); } } } foreach ($class->referrersMappings as $name => $referrerMapping) { if ($this->originalData[$oid][$name]) { foreach ($this->originalData[$oid][$name] as $referrer) { $this->computeReferrerChanges($referrer); } } } }