private function doMerge($document, array &$visited, $prevManagedCopy = null, $assoc = null) { $oid = spl_object_hash($document); if (isset($visited[$oid])) { return $document; // Prevent infinite recursion } $visited[$oid] = $document; // mark visited $class = $this->dm->getClassMetadata(get_class($document)); $locale = $this->getCurrentLocale($document, $class); // First we assume DETACHED, although it can still be NEW but we can avoid // an extra db-roundtrip this way. If it is not MANAGED but has an identity, // we need to fetch it from the db anyway in order to merge. // MANAGED entities are ignored by the merge operation. if ($this->getDocumentState($document) == self::STATE_MANAGED) { $managedCopy = $document; } else { $id = $this->determineDocumentId($document, $class); $persist = false; if (!$id) { // document is new $managedCopy = $class->newInstance(); $persist = true; } else { $managedCopy = $this->getDocumentById($id); if ($managedCopy) { // We have the document in-memory already, just make sure its not removed. if ($this->getDocumentState($managedCopy) == self::STATE_REMOVED) { throw new InvalidArgumentException("Removed document detected during merge at '{$id}'. Cannot merge with a removed document."); } if (ClassUtils::getClass($managedCopy) != ClassUtils::getClass($document)) { throw new InvalidArgumentException('Can not merge documents of different classes.'); } if ($this->getCurrentLocale($managedCopy, $class) !== $locale) { $this->doLoadTranslation($document, $class, $locale, true); } } elseif ($locale) { // We need to fetch the managed copy in order to merge. $managedCopy = $this->dm->findTranslation($class->name, $id, $locale); } else { // We need to fetch the managed copy in order to merge. $managedCopy = $this->dm->find($class->name, $id); } if ($managedCopy === null) { // If the identifier is ASSIGNED, it is NEW, otherwise an error // since the managed document was not found. if ($class->idGenerator !== ClassMetadata::GENERATOR_TYPE_ASSIGNED) { throw new InvalidArgumentException("Document not found in merge operation: {$id}"); } $managedCopy = $class->newInstance(); $class->setIdentifierValue($managedCopy, $id); $persist = true; } } $managedOid = spl_object_hash($managedCopy); // Merge state of $document into existing (managed) document foreach ($class->reflFields as $fieldName => $prop) { $other = $prop->getValue($document); if ($other instanceof PersistentCollection && !$other->isInitialized() || $other instanceof Proxy && !$other->__isInitialized()) { // do not merge fields marked lazy that have not been fetched. // keep the lazy persistent collection of the managed copy. continue; } $mapping = $class->mappings[$fieldName]; if (ClassMetadata::MANY_TO_ONE === $mapping['type']) { $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $mapping); } elseif (ClassMetadata::MANY_TO_MANY === $mapping['type']) { $managedCol = $prop->getValue($managedCopy); if (!$managedCol) { $managedCol = new ReferenceManyCollection($this->dm, $document, $mapping['property'], array(), isset($mapping['targetDocument']) ? $mapping['targetDocument'] : null, $locale, $this->getReferenceManyCollectionTypeFromMetadata($mapping)); $prop->setValue($managedCopy, $managedCol); $this->originalData[$managedOid][$fieldName] = $managedCol; } $this->cascadeMergeCollection($managedCol, $mapping); } elseif ('child' === $mapping['type']) { if (null !== $other) { $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $mapping); } } elseif ('children' === $mapping['type']) { $managedCol = $prop->getValue($managedCopy); if (!$managedCol) { $managedCol = new ChildrenCollection($this->dm, $managedCopy, $mapping['filter'], $mapping['fetchDepth'], $locale); $prop->setValue($managedCopy, $managedCol); $this->originalData[$managedOid][$fieldName] = $managedCol; } $this->cascadeMergeCollection($managedCol, $mapping); } elseif ('referrers' === $mapping['type']) { $managedCol = $prop->getValue($managedCopy); if (!$managedCol) { $referringMeta = $this->dm->getClassMetadata($mapping['referringDocument']); $referringField = $referringMeta->mappings[$mapping['referencedBy']]; $managedCol = new ReferrersCollection($this->dm, $managedCopy, $referringField['strategy'], $referringField['property'], $locale); $prop->setValue($managedCopy, $managedCol); $this->originalData[$managedOid][$fieldName] = $managedCol; } $this->cascadeMergeCollection($managedCol, $mapping); } elseif ('mixedreferrers' === $mapping['type']) { $managedCol = $prop->getValue($managedCopy); if (!$managedCol) { $managedCol = new ImmutableReferrersCollection($this->dm, $managedCopy, $mapping['referenceType'], $locale); $prop->setValue($managedCopy, $managedCol); $this->originalData[$managedOid][$fieldName] = $managedCol; } $this->cascadeMergeCollection($managedCol, $mapping); } elseif ('parent' === $mapping['type']) { $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $mapping); } elseif (in_array($mapping['type'], array('locale', 'versionname', 'versioncreated', 'node', 'nodename'))) { if (null !== $other) { $prop->setValue($managedCopy, $other); } } elseif (!$class->isIdentifier($fieldName)) { $prop->setValue($managedCopy, $other); } } if ($persist) { $this->persistNew($class, $managedCopy); } // Mark the managed copy visited as well $visited[$managedOid] = true; } if ($prevManagedCopy !== null) { $prevClass = $this->dm->getClassMetadata(get_class($prevManagedCopy)); if ($assoc['type'] == ClassMetadata::MANY_TO_ONE) { $prevClass->reflFields[$assoc['fieldName']]->setValue($prevManagedCopy, $managedCopy); } else { $prevClass->reflFields[$assoc['fieldName']]->getValue($prevManagedCopy)->add($managedCopy); } } $this->cascadeMerge($class, $document, $managedCopy, $visited); return $managedCopy; }
/** * {@inheritDoc} */ public function findTranslation($className, $id, $locale, $fallback = true) { return $this->wrapped->findTranslation($className, $id, $locale, $fallback); }