Cascades the save operation to associated entities.

@param object   $entity
@param object[] $visited

@throws ORMInvalidArgumentException
327837
private function cascadePersist($entity, array &$visited)
    {
        $class = $this->em->getClassMetadata(get_class($entity));

        if ($entity instanceof GhostObjectInterface && ! $entity->isProxyInitialized()) {
            // nothing to do - proxy is not initialized, therefore we don't do anything with it
            return;
        }

        foreach ($class->getDeclaredPropertiesIterator() as $association) {
            if (! ($association instanceof AssociationMetadata && in_array('persist', $association->getCascade(), true))) {
                continue;
            }

            /** @var AssociationMetadata $association */
            $relatedEntities = $association->getValue($entity);
            $targetEntity    = $association->getTargetEntity();

            switch (true) {
                case $relatedEntities instanceof PersistentCollection:
                    // Unwrap so that foreach() does not initialize
                    $relatedEntities = $relatedEntities->unwrap();
                    // break; is commented intentionally!

                case $relatedEntities instanceof Collection:
                case is_array($relatedEntities):
                    if (! ($association instanceof ToManyAssociationMetadata)) {
                        throw ORMInvalidArgumentException::invalidAssociation(
                            $this->em->getClassMetadata($targetEntity),
                            $association,
                            $relatedEntities
                        );
                    }

                    foreach ($relatedEntities as $relatedEntity) {
                        $this->doPersist($relatedEntity, $visited);
                    }

                    break;

                case $relatedEntities !== null:
                    if (! $relatedEntities instanceof $targetEntity) {
                        throw ORMInvalidArgumentException::invalidAssociation(
                            $this->em->getClassMetadata($targetEntity),
                            $association,
                            $relatedEntities
                        );
                    }

                    $this->doPersist($relatedEntities, $visited);
                    break;

                default:
                    // Do nothing
            }
        }
    }
448898
public function processRecords(Entity $entity, array $with, $records, $readOnly)
    {
        $pkField = $entity->getPrimaryKey();

        $objects          = [];
        $currentKey       = null;
        $object           = null;
        $recordsToProcess = [];

        $relations = array_map(
            [$entity, 'getRelation'],
            array_intersect($with, $entity->getRelationNames())
        );

        foreach ($records as $record) {
            //Extract columns that are relevant for the current metadata
            $key = $record[ $pkField ];
            if ($currentKey !== $key) {
                if ($object !== null) {
                    //Process and save the previous object
                    $this->processRelated(
                        $entity,
                        $object,
                        $readOnly,
                        $recordsToProcess,
                        $relations,
                        $with
                    );
                    $recordsToProcess       = [];
                    $objects[ $currentKey ] = $object;
                }
                $currentKey = $key;
                $object     = $this->createObject(
                    $entity,
                    $record,
                    $relations,
                    $readOnly
                );
            }
            //Store the record to be processed for the related entities
            $recordsToProcess[] = $record;
        }
        if ($object !== null) {
            //Process and save the last object
            $this->processRelated(
                $entity,
                $object,
                $readOnly,
                $recordsToProcess,
                $relations,
                $with
            );
            $objects[ $key ] = $object;
        }

        return $objects;
    }
334915
private function cascadeRemove($document, array &$visited)
    {
        $class = $this->dm->getClassMetadata(get_class($document));
        foreach ($class->propertyMetadata as $mapping) {
            if (!$mapping->isCascadeRemove && !$mapping->orphanRemoval) {
                continue;
            }

            if ($document instanceof Proxy && !$document->__isInitialized__) {
                $document->__load();
            }

            $relatedDocuments = $class->getPropertyMetadata($mapping->name)->getValue($document);
            if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
                // If its a PersistentCollection initialization is intended! No unwrap!
                foreach ($relatedDocuments as $relatedDocument) {
                    $this->doRemove($relatedDocument, $visited);
                }
            } elseif ($relatedDocuments !== null) {
                $this->doRemove($relatedDocuments, $visited);
            }
        }
    }
66038
private function doDetach($object, array &$visited)
    {
        $oid = spl_object_hash($object);
        if (isset($visited[$oid])) {
            return;
        }

        $visited[$oid] = true;

        $state = $this->getDocumentState($object, self::STATE_DETACHED);
        if (self::STATE_MANAGED !== $state) {
            return;
        }

        unset(
            $this->documentStates[$oid],
            $this->objects[$oid],
            $this->originalDocumentData[$oid]
        );

        $this->removeFromIdentityMap($object);
        $this->cascadeDetach($object, $visited);
    }
769074
private function cascadeDetach(ClassMetadata $class, $document, array &$visited)
    {
        foreach ($class->childrenMappings as $fieldName) {
            $mapping = $class->mappings[$fieldName];
            if (!($mapping['cascade'] & ClassMetadata::CASCADE_DETACH)) {
                continue;
            }
            $related = $class->reflFields[$fieldName]->getValue($document);
            if ($related instanceof Collection || is_array($related)) {
                foreach ($related as $relatedDocument) {
                    $this->doDetach($relatedDocument, $visited);
                }
            } elseif ($related !== null) {
                $this->doDetach($related, $visited);
            }
        }

        foreach (array_merge($class->referenceMappings, $class->referrersMappings) as $fieldName) {
            $mapping = $class->mappings[$fieldName];
            if (!($mapping['cascade'] & ClassMetadata::CASCADE_DETACH)) {
                continue;
            }
            $related = $class->reflFields[$fieldName]->getValue($document);
            if ($related instanceof Collection || is_array($related)) {
                foreach ($related as $relatedDocument) {
                    $this->doDetach($relatedDocument, $visited);
                }
            } elseif ($related !== null) {
                $this->doDetach($related, $visited);
            }
        }

        // no cascade for mixed referrers
    }
610418
public function insert($entity)
    {
        $data     = $this->builder->reduce($entity);
        $entityId = $this->entityRegistry->getEntityId($entity);

        #try
        #{
        $this->connection->insert($this->tableName, $data);

        if (empty($entityId)) {
            $this->entityRegistry->setEntityId($entity, $this->connection->lastInsertId());
        }

        $this->builder->resolve($entity);
        #}
        #catch (\Exception $e)
        #{
        #	throw new OrmException("Entity with id {$entityId} already exists.\n" . $e->getMessage(), 0, $e);
        #}
    }
505365
private function cascadeRemove($entity, array &$visited)
    {
        $entitiesToCascade = [];
        $class             = $this->em->getClassMetadata(get_class($entity));

        foreach ($class->getDeclaredPropertiesIterator() as $association) {
            if (! ($association instanceof AssociationMetadata && in_array('remove', $association->getCascade(), true))) {
                continue;
            }

            if ($entity instanceof GhostObjectInterface && ! $entity->isProxyInitialized()) {
                $entity->initializeProxy();
            }

            $relatedEntities = $association->getValue($entity);

            switch (true) {
                case $relatedEntities instanceof Collection:
                case is_array($relatedEntities):
                    // If its a PersistentCollection initialization is intended! No unwrap!
                    foreach ($relatedEntities as $relatedEntity) {
                        $entitiesToCascade[] = $relatedEntity;
                    }
                    break;

                case $relatedEntities !== null:
                    $entitiesToCascade[] = $relatedEntities;
                    break;

                default:
                    // Do nothing
            }
        }

        foreach ($entitiesToCascade as $relatedEntity) {
            $this->doRemove($relatedEntity, $visited);
        }
    }
1173170
private function cascadePersist($document, array &$visited)
    {
        $class = $this->dm->getClassMetadata(get_class($document));

        foreach ($class->associationMappings as $fieldName => $mapping) {
            if ( ! $mapping['isCascadePersist']) {
                continue;
            }

            $relatedDocuments = $class->reflFields[$fieldName]->getValue($document);

            if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
                if ($relatedDocuments instanceof PersistentCollection) {
                    // Unwrap so that foreach() does not initialize
                    $relatedDocuments = $relatedDocuments->unwrap();
                }

                foreach ($relatedDocuments as $relatedDocument) {
                    $this->doPersist($relatedDocument, $visited);
                }
            } elseif ($relatedDocuments !== null) {
                $this->doPersist($relatedDocuments, $visited);
            }
        }
    }
963429
public function refresh($entity)
    {
        if (! is_object($entity)) {
            throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()', $entity);
        }

        $this->errorIfClosed();

        $this->unitOfWork->refresh($entity);
    }
273004
private function doRefresh($document, array &$visited)
    {
        $oid = spl_object_hash($document);
        if (isset($visited[$oid])) {
            return; // Prevent infinite recursion
        }

        $visited[$oid] = $document; // mark visited

        $class = $this->dm->getClassMetadata(get_class($document));

        if (!$class->isEmbeddedDocument) {
            if ($this->getDocumentState($document) !== self::STATE_MANAGED) {
                throw new \InvalidArgumentException('Document is not MANAGED.');
            }

            $identifier = $this->documentIdentifiers[$oid];
            $this->getDocumentPersister($class->name)->refresh($identifier, $document);
        }

        $this->cascadeRefresh($document, $visited);
    }
957824
public function revert(&$entity, $revision)
    {
        /** @var ClassMetadataInfo $meta */
        $meta = $this->getClassMetadata(get_class($entity));

        $data = $this->getRevisionData($entity, ['revision_id = :revisionId'], ['revisionId' => $revision], 1);

        /** @var \ReflectionProperty[] $revisedProperties */
        $revisedProperties = $this->annotationReader->getRevisedProperties($entity);

        $exception = null;
        foreach ($revisedProperties as $field => $property) {
            if (! key_exists($field, $revisedProperties)) {
                continue;
            }

            if (isset($meta->fieldMappings[$field])) {
                $columnName = $meta->fieldMappings[$field]['columnName'];
            } elseif (isset($meta->associationMappings[$field])) {
                $columnName = $meta->associationMappings[$field]['joinColumns'][0]['name'];
            } else {
                throw new \Exception('Cannot find column name for' . $field);
            }

            $value = $data[$columnName];

            $property = $meta->getReflectionProperty($field);
            $property->setValue($entity, $value);

            if ($field == 'deletedAt' && $value !== null) {
                $exception = new DeletedException('Entity is deleted in this revision');
            }
        }

        if ($exception) {
            $exception->setEntity($entity);
            throw $exception;
        }

        return $entity;
    }
809429
private function doRefresh($xmlEntity, array &$visited)
    {
        $oid = spl_object_hash($xmlEntity);
        if (isset($visited[$oid])) {
            return; // Prevent infinite recursion
        }

        $visited[$oid] = $xmlEntity; // mark visited

        $class = $this->xem->getClassMetadata(get_class($xmlEntity));
        if ($this->getXmlEntityState($xmlEntity) == self::STATE_MANAGED) {
            // @todo refresh xml-entity
        } else {
            throw new \InvalidArgumentException("XmlEntity is not MANAGED.");
        }
    }
954068
protected function _saveRevision(Entity $entity)
    {
        if ($entity->isNew()) {
            return;
        }

        try {
            $prev = TableRegistry::get('Content.Contents')->get($entity->id);
            $hash = $this->_calculateHash($prev);
            $exists = $this->ContentRevisions->exists([
                'ContentRevisions.content_id' => $entity->id,
                'ContentRevisions.hash' => $hash,
            ]);

            if (!$exists) {
                $revision = $this->ContentRevisions->newEntity([
                    'content_id' => $prev->id,
                    'summary' => $entity->get('edit_summary'),
                    'data' => $prev,
                    'hash' => $hash,
                ]);

                if (!$this->ContentRevisions->hasBehavior('Timestamp')) {
                    $this->ContentRevisions->addBehavior('Timestamp');
                }
                $this->ContentRevisions->save($revision);
            }
        } catch (\Exception $ex) {
            // unable to create content's review
        }
    }
700607
public function save($entity) {
        $this->load();
        $mapper = MapperBuilder::buildFromClassName($this->mapping, get_class($entity));
        if (is_null($entity->getId())) {
            $affectedRows = $this->insertEntity($entity, $mapper);
        } else {
            $affectedRows = $this->updateEntity($entity, $mapper);
        }
        if (0 < $affectedRows) {
            return true;
        } else {
            return false;
        }
    }
1282778
public function save($entity, $options = [])
    {
        if ($this->link() !== static::LINK_KEY) {
            return true;
        }

        $name = $this->name();
        if (!isset($entity->{$name})) {
            return true;
        }

        $conditions = $this->match($entity);
        $related = $entity->{$name};
        $result = !!$related->set($conditions)->save($options);
        return $result;
    }
791874
public function delete($entity)
    {
        $identifier   = $this->getIdentifier($entity);
        $affectedRows = $this->connection->delete($this->tableName, $identifier);

        if ($affectedRows == 0) {
            throw new OrmException("Entity not found.");
        }
    }
98313
private function cascadeRemove($document, array &$visited)
    {
        $class = $this->dm->getClassMetadata(get_class($document));
        foreach ($class->fieldMappings as $mapping) {
            if ( ! $mapping['isCascadeRemove']) {
                continue;
            }
            if ($document instanceof Proxy && ! $document->__isInitialized__) {
                $document->__load();
            }
            if (isset($mapping['embedded'])) {
                $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document);
                if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) {
                    // If its a PersistentCollection initialization is intended! No unwrap!
                    foreach ($relatedDocuments as $relatedDocument) {
                        $this->cascadeRemove($relatedDocument, $visited);
                    }
                } elseif ($relatedDocuments !== null) {
                    $this->cascadeRemove($relatedDocuments, $visited);
                }
            } elseif (isset($mapping['reference'])) {
                $relatedDocuments = $class->reflFields[$mapping['fieldName']]->getValue($document);
                if (($relatedDocuments instanceof Collection || is_array($relatedDocuments))) {
                    // If its a PersistentCollection initialization is intended! No unwrap!
                    foreach ($relatedDocuments as $relatedDocument) {
                        $this->doRemove($relatedDocument, $visited);
                    }
                } elseif ($relatedDocuments !== null) {
                    $this->doRemove($relatedDocuments, $visited);
                }
            }
        }
    }