/** * Use the name and parent fields to generate the id * * {@inheritDoc} */ public function generate($document, ClassMetadata $class, DocumentManager $dm, $parent = null) { if (null === $parent) { $parent = $class->parentMapping ? $class->getFieldValue($document, $class->parentMapping) : null; } $name = $class->nodename ? $class->getFieldValue($document, $class->nodename) : null; $id = $class->getFieldValue($document, $class->identifier); if (empty($id)) { if (empty($name) && empty($parent)) { throw IdException::noIdentificationParameters($document, $class->parentMapping, $class->nodename); } if (empty($parent)) { throw IdException::noIdNoParent($document, $class->parentMapping); } if (empty($name)) { throw IdException::noIdNoName($document, $class->nodename); } } // use assigned ID by default if (empty($parent) || empty($name)) { return $id; } if ($exception = $class->isValidNodename($name)) { throw IdException::illegalName($document, $class->nodename, $name); } // determine ID based on the path and the node name return $this->buildName($document, $class, $dm, $parent, $name); }
/** * 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(); } }