Cascades the preRemove event to embedded documents.

@param ClassMetadata $class
@param object $document
343330
private function cascadePreRemove(ClassMetadata $class, $document)
    {
        $hasPreRemoveListeners = $this->evm->hasListeners(Events::preRemove);

        foreach ($class->fieldMappings as $mapping) {
            if (isset($mapping['embedded'])) {
                $value = $class->reflFields[$mapping['fieldName']]->getValue($document);
                if ($value === null) {
                    continue;
                }
                if ($mapping['type'] === 'one') {
                    $value = array($value);
                }
                foreach ($value as $entry) {
                    $entryClass = $this->dm->getClassMetadata(get_class($entry));
                    if ( ! empty($entryClass->lifecycleCallbacks[Events::preRemove])) {
                        $entryClass->invokeLifecycleCallbacks(Events::preRemove, $entry);
                    }
                    if ($hasPreRemoveListeners) {
                        $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entry, $this->dm));
                    }
                    $this->cascadePreRemove($entryClass, $entry);
                }
            }
        }
    }
953984
private function executeUpserts(ClassMetadata $class, array $documents, array $options = []) : void
    {
        $persister = $this->getDocumentPersister($class->name);

        foreach ($documents as $oid => $document) {
            $persister->addUpsert($document);
            unset($this->documentUpserts[$oid]);
        }

        $persister->executeUpserts($options);

        foreach ($documents as $document) {
            $this->lifecycleEventManager->postPersist($class, $document);
        }
    }
713308
public function setParentAssociation(object $document, array $mapping, ?object $parent, string $propertyPath) : void
    {
        $oid                                   = spl_object_hash($document);
        $this->embeddedDocumentsRegistry[$oid] = $document;
        $this->parentAssociations[$oid]        = [$mapping, $parent, $propertyPath];
    }
870762
public function scheduleForDelete(object $document) : void
    {
        $oid = spl_object_hash($document);

        if (isset($this->documentInsertions[$oid])) {
            if ($this->isInIdentityMap($document)) {
                $this->removeFromIdentityMap($document);
            }
            unset($this->documentInsertions[$oid]);
            return; // document has not been persisted yet, so nothing more to do.
        }

        if (! $this->isInIdentityMap($document)) {
            return; // ignore
        }

        $this->removeFromIdentityMap($document);
        $this->documentStates[$oid] = self::STATE_REMOVED;

        if (isset($this->documentUpdates[$oid])) {
            unset($this->documentUpdates[$oid]);
        }
        if (isset($this->documentDeletions[$oid])) {
            return;
        }

        $this->documentDeletions[$oid] = $document;
    }
935043
private function newInstance(DocumentMetadata $class)
    {
        $document = $class->newInstance();

        // inject ObjectManager upon refresh.
        if ($document instanceof ObjectManagerAware) {
            $document->injectObjectManager($this->manager, $class);
        }

        return $document;
    }
418267
public function mapManyEmbedded(ClassMetadata $class, $type = Type::LIST_)
    {
        $this->embedded = true;
        $this->type     = $type;

        $this->mapAttribute($class);
    }
456731
public function scheduleForDirtyCheck($document)
    {
        $class = $this->dm->getClassMetadata(get_class($document));
        $oid   = spl_object_hash($document);

        $this->scheduledForDirtyCheck[$class->name][$oid] = $document;
    }
946403
private function executeDeletions(ClassMetadata $class, array $options = array())
    {
        $hasPostRemoveLifecycleCallbacks = ! empty($class->lifecycleCallbacks[Events::postRemove]);
        $hasPostRemoveListeners = $this->evm->hasListeners(Events::postRemove);

        $className = $class->name;
        $persister = $this->getDocumentPersister($className);
        $collection = $this->dm->getDocumentCollection($className);
        foreach ($this->documentDeletions as $oid => $document) {
            if (get_class($document) == $className || $document instanceof Proxy && $document instanceof $className) {
                if ( ! $class->isEmbeddedDocument) {
                    $persister->delete($document, $options);
                }
                unset(
                    $this->documentDeletions[$oid],
                    $this->documentIdentifiers[$oid],
                    $this->originalDocumentData[$oid]
                );

                // Clear snapshot information for any referenced PersistentCollection
                // http://www.doctrine-project.org/jira/browse/MODM-95
                foreach ($class->fieldMappings as $fieldMapping) {
                    if (isset($fieldMapping['type']) && $fieldMapping['type'] === 'many') {
                        $value = $class->reflFields[$fieldMapping['fieldName']]->getValue($document);
                        if ($value instanceof PersistentCollection) {
                            $value->clearSnapshot();
                        }
                    }
                }

                // Document with this $oid after deletion treated as NEW, even if the $oid
                // is obtained by a new document because the old one went out of scope.
                $this->documentStates[$oid] = self::STATE_NEW;

                if ($hasPostRemoveLifecycleCallbacks) {
                    $class->invokeLifecycleCallbacks(Events::postRemove, $document);
                }
                if ($hasPostRemoveListeners) {
                    $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($document, $this->dm));
                }
                $this->cascadePostRemove($class, $document);
            }
        }
    }
1075518
public function prepareUpsertData($document)
    {
        $class     = $this->dm->getClassMetadata(get_class($document));
        $changeset = $this->uow->getDocumentChangeSet($document);

        $updateData = [];
        foreach ($changeset as $fieldName => $change) {
            $mapping = $class->fieldMappings[$fieldName];

            [$old, $new] = $change;

            // Scalar fields
            if (! isset($mapping['association'])) {
                if ($new !== null) {
                    if (empty($mapping['id']) && isset($mapping['strategy']) && $mapping['strategy'] === ClassMetadata::STORAGE_STRATEGY_INCREMENT) {
                        $operator = '$inc';
                        $value    = Type::getType($mapping['type'])->convertToDatabaseValue($new - $old);
                    } else {
                        $operator = '$set';
                        $value    = Type::getType($mapping['type'])->convertToDatabaseValue($new);
                    }

                    $updateData[$operator][$mapping['name']] = $value;
                } elseif ($mapping['nullable'] === true) {
                    $updateData['$setOnInsert'][$mapping['name']] = null;
                }

            // @EmbedOne
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::EMBED_ONE) {
                // If we don't have a new value then do nothing on upsert
                // If we have a new embedded document then lets set the whole thing
                if ($new && $this->uow->isScheduledForInsert($new)) {
                    $updateData['$set'][$mapping['name']] = $this->prepareEmbeddedDocumentValue($mapping, $new);
                } elseif ($new) {
                    // Update existing embedded document
                    $update = $this->prepareUpsertData($new);
                    foreach ($update as $cmd => $values) {
                        foreach ($values as $key => $value) {
                            $updateData[$cmd][$mapping['name'] . '.' . $key] = $value;
                        }
                    }
                }

            // @ReferenceOne
            } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE) {
                if (isset($new) || $mapping['nullable'] === true) {
                    $updateData['$set'][$mapping['name']] = $new === null ? null : $this->prepareReferencedDocumentValue($mapping, $new);
                }

            // @ReferenceMany, @EmbedMany
            } elseif ($mapping['type'] === ClassMetadata::MANY && ! $mapping['isInverseSide']
                    && $new instanceof PersistentCollectionInterface && $new->isDirty()
                    && CollectionHelper::isAtomic($mapping['strategy'])) {
                $updateData['$set'][$mapping['name']] = $this->prepareAssociatedCollectionValue($new, true);
            }
            // @EmbedMany and @ReferenceMany are handled by CollectionPersister
        }

        // add discriminator if the class has one
        if (isset($class->discriminatorField)) {
            $discriminatorValue = $class->discriminatorValue;

            if ($discriminatorValue === null) {
                if (! empty($class->discriminatorMap)) {
                    throw MappingException::unlistedClassInDiscriminatorMap($class->name);
                }

                $discriminatorValue = $class->name;
            }

            $updateData['$set'][$class->discriminatorField] = $discriminatorValue;
        }

        return $updateData;
    }
755292
private function cascadeMerge(object $document, object $managedCopy, array &$visited) : void
    {
        $class = $this->dm->getClassMetadata(get_class($document));

        $associationMappings = array_filter(
            $class->associationMappings,
            static function ($assoc) {
                return $assoc['isCascadeMerge'];
            }
        );

        foreach ($associationMappings as $assoc) {
            $relatedDocuments = $class->reflFields[$assoc['fieldName']]->getValue($document);

            if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
                if ($relatedDocuments === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
                    // Collections are the same, so there is nothing to do
                    continue;
                }

                foreach ($relatedDocuments as $relatedDocument) {
                    $this->doMerge($relatedDocument, $visited, $managedCopy, $assoc);
                }
            } elseif ($relatedDocuments !== null) {
                $this->doMerge($relatedDocuments, $visited, $managedCopy, $assoc);
            }
        }
    }
1210713
private function cascadePostPersist(ClassMetadata $class, object $document) : void
    {
        foreach ($class->getEmbeddedFieldsMappings() as $mapping) {
            $value = $class->reflFields[$mapping['fieldName']]->getValue($document);
            if ($value === null) {
                continue;
            }
            $values = $mapping['type'] === ClassMetadata::ONE ? [$value] : $value;
            foreach ($values as $embeddedDocument) {
                $this->postPersist($this->dm->getClassMetadata(get_class($embeddedDocument)), $embeddedDocument);
            }
        }
    }
305449
private function cascadePersist(object $document, array &$visited) : void
    {
        $class = $this->dm->getClassMetadata(get_class($document));

        $associationMappings = array_filter(
            $class->associationMappings,
            static function ($assoc) {
                return $assoc['isCascadePersist'];
            }
        );

        foreach ($associationMappings as $fieldName => $mapping) {
            $relatedDocuments = $class->reflFields[$fieldName]->getValue($document);

            if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
                if ($relatedDocuments instanceof PersistentCollectionInterface) {
                    if ($relatedDocuments->getOwner() !== $document) {
                        $relatedDocuments = $this->fixPersistentCollectionOwnership($relatedDocuments, $document, $class, $mapping['fieldName']);
                    }
                    // Unwrap so that foreach() does not initialize
                    $relatedDocuments = $relatedDocuments->unwrap();
                }

                $count = 0;
                foreach ($relatedDocuments as $relatedKey => $relatedDocument) {
                    if (! empty($mapping['embedded'])) {
                        [, $knownParent ] = $this->getParentAssociation($relatedDocument);
                        if ($knownParent && $knownParent !== $document) {
                            $relatedDocument               = clone $relatedDocument;
                            $relatedDocuments[$relatedKey] = $relatedDocument;
                        }
                        $pathKey = CollectionHelper::isList($mapping['strategy']) ? $count++ : $relatedKey;
                        $this->setParentAssociation($relatedDocument, $mapping, $document, $mapping['fieldName'] . '.' . $pathKey);
                    }
                    $this->doPersist($relatedDocument, $visited);
                }
            } elseif ($relatedDocuments !== null) {
                if (! empty($mapping['embedded'])) {
                    [, $knownParent ] = $this->getParentAssociation($relatedDocuments);
                    if ($knownParent && $knownParent !== $document) {
                        $relatedDocuments = clone $relatedDocuments;
                        $class->setFieldValue($document, $mapping['fieldName'], $relatedDocuments);
                    }
                    $this->setParentAssociation($relatedDocuments, $mapping, $document, $mapping['fieldName']);
                }
                $this->doPersist($relatedDocuments, $visited);
            }
        }
    }
150275
public function unscheduleOrphanRemoval(object $document) : void
    {
        $oid = spl_object_hash($document);
        unset($this->orphanRemovals[$oid]);
    }
256679
public function postPersist(ClassMetadata $class, $document)
    {
        $class->invokeLifecycleCallbacks(Event::postPersist, $document, [new LifecycleEventArgs($document, $this->dm)]);
        $this->evm->dispatchEvent(Event::postPersist, new LifecycleEventArgs($document, $this->dm));
        $this->cascadePostPersist($class, $document);
    }
689497
public function delete($document): void
    {
        $class = $this->dm->getClassMetadata(get_class($document));
        $id = $class->getSingleIdentifier($document);

        $this->collection->delete((string) $id);
    }
654698
public function prepareAssociatedDocumentValue(PropertyMetadata $mapping, $document, $includeNestedCollections = false)
    {
        if ($document === null) {
            return Type::getNull();
        }

        if ($mapping->embedded) {
            return $this->prepareEmbeddedDocumentValue($mapping, $document, $includeNestedCollections);
        }

        if ($mapping->reference) {
            $class      = $this->dm->getClassMetadata(get_class($document));
            $identifier = $class->createIdentifier($document);

            return $this->prepareReferencedDocumentValue($mapping, $identifier);
        }

        throw new \InvalidArgumentException('Mapping is neither embedded nor reference.');
    }
434306
public function scheduleForSynchronization(object $document) : void
    {
        $class                                                                       = $this->dm->getClassMetadata(get_class($document));
        $this->scheduledForSynchronization[$class->name][spl_object_hash($document)] = $document;
    }