Duplicates a single has_one relation from one object to another.
Note: Child object will be force written.

@param DataObject $sourceObject
@param DataObject $destinationObject
@param string $relation
178231
protected function duplicateHasOneRelation($sourceObject, $destinationObject, $relation)
    {
        // Check if original object exists
        $item = $sourceObject->getComponent($relation);
        if (!$item->isInDB()) {
            return;
        }

        $clonedItem = $item->duplicate(false);
        $destinationObject->setComponent($relation, $clonedItem);
    }
839299
protected static function mergeObjects(stdClass &$target, stdClass ...$sources): void
    {
        foreach ($sources as $source) {
            foreach ($source as $key => $value) {
                if (str_starts_with($key, 'i__')) {
                    continue;
                }

                if (isset($target->$key) && is_object($value) && is_object($target->$key)) {
                    static::mergeObjects($target->$key, $value);
                } else {
                    $target->$key = $value;
                }
            }
        }
    }
321932
protected function synchronize(string $relation)
    {
        $actualContent = $this->relationships[$relation];

        $relationshipObject = $this->entityMap->$relation($this->getEntityObject());
        $relationshipObject->setParent($this->wrappedEntity);
        $relationshipObject->sync($actualContent);
    }
915336
private function mergeObject($destinationObject, $sourceObject)
    {
        $reflectedDestination  = new \ReflectionObject($destinationObject);
        $reflectedSource       = new \ReflectionObject($sourceObject);
        $defaultPropertyValues = $reflectedDestination->getDefaultProperties();

        foreach ($reflectedSource->getProperties() as $sourceProperty) {
            $destinationObject = $this->mergeProperty(
                $destinationObject,
                $reflectedDestination->getProperty($sourceProperty->getName()),
                $sourceObject,
                $sourceProperty,
                $defaultPropertyValues
            );
        }

        return $destinationObject;
    }
184855
public function move(string $sourceInterface, string $sourceName, string $targetInterface, string $targetName)
    {
        $sourceIndex = $sourceInterface . '-' . $sourceName;
        if (! isset($this->container[$sourceIndex])) {
            throw $this->unbound($sourceIndex);
        }
        $targetIndex = $targetInterface . '-' . $targetName;
        $this->container[$targetIndex] = $this->container[$sourceIndex];
        unset($this->container[$sourceIndex]);
    }
1212414
public function sdiffstore($destination, array $sources) {
        try {
            return call_user_func_array(array(
                $this->getRedis(),
                'sDiffStore',
            ), array_merge(array($destination), $sources));
        } catch (RedisException $ex) {
            throw new ConnectException();
        }
    }
915764
public function insertChild ($relationIdentifier, $id, $row, $foreignKey = null)
		{
			$relation = $this->getRelation($relationIdentifier);

			if (! $relation) {
				throw new RelationException('Invalid relation `' . $relationIdentifier);
			}

			if ($relation instanceof OneToMany || $relation instanceof OneToOne) {
				if (is_null($foreignKey)) {
					$foreignKey = $this->getPrimaryKey();
				}

				$row[$foreignKey] = $id;

				$this->database->createQuery()
					->insert()
					->into($relation->getRelatedTable())
					->set($row)
					->execute();

				return $this->database->insertId();

			} else if ($relation instanceof ManyToMany) {
				if (is_null($foreignKey)) {
					$foreignKey = $this->getPrimaryKey();
				}

				$childId = $this->database->createQuery()
					->insert()
					->into($relation->getRelatedTable())
					->set($row)
					->execute();

				$data = array(
					$foreignKey => $id,
					$this->database->primaryKey($relation->getRelatedTable()) => $childId,
				);

				$this->database->createQuery()
					->insert()
					->into($relation->getRelationalTable())
					->set($data)
					->execute();

				return $childId;

			} else {
				throw new RelationException('Invalid relation type `' . gettype($relation) . '`');
			}
		}
1285819
public static function buildRelationQuery($entity, Relation $relation) {
        $relQuery = "";
        $getid = "getId";
        if ($relation->getCardinality() == Relation::$manyToMany) {
            $m = "get" . $relation->getName();
            $relQuery .= "INSERT INTO " . $relation->getTable() . " (" . $relation->getLocalColumn() . ", " . $relation->getForeignColumn() . ") ";
            $relQuery .= "VALUES (:" . $relation->getLocalColumn() . ", :" . $relation->getForeignColumn() . ");";
        }
        if ($relation->getCardinality() == Relation::$oneToMany) {
            $relMapper = MapperBuilder::buildFromName($this->mapping, $relation->getEntity());
            $m = "get" . $relation->getName();
            foreach ($entity->$m() as $rel) {
                $setid = "set" . $relMapper->getNameForColumn($relation->getForeignColumn());
                $rel->$setid($entity->$getid());
                $relQuery .= $this->buildInsertQuery($rel);
            }
        }


        return $relQuery;
    }
1271564
public function assignRelated($source, $relationNames=null, $meta=null)
    {
        if (!$source) { return; }

        $stack = [];
        $sourceIsArray = is_array($source) || $source instanceof \Traversable;
        if (!$sourceIsArray) {
            $source = array($source);
        }

        if ($meta !== null) {
            $meta = !$meta instanceof Meta ? $this->mapper->getMeta($meta) : $meta;
        } else {
            $meta = $this->mapper->getMeta(get_class($source[0]));
        }

        if (!$relationNames) {
            if ($meta->autoRelations) {
                $relationNames = $meta->autoRelations;
            } else {
                throw new Exception("relationNames not passed, class {$meta->class} does not define autoRelations");
            }
        }

        $relationMap = [];
        foreach ((array)$relationNames as $relationName) {
            if (!isset($meta->relations[$relationName])) {
                throw new Exception("Unknown relation $relationName on {$meta->class}");
            }
            $relationMap[$relationName] = $rel = $meta->relations[$relationName];
            if ($rel['mode'] == 'class') {
                throw new Exception("Relation $relationName is not assignable for class {$meta->class}");
            }
        }

        $done = [];
        foreach ($relationMap as $relationName=>$relation) {
            if (isset($done[$relationName])) {
                continue;
            }
            $done[$relationName] = true;

            $missing = [];
            foreach ($source as $idx=>$item) {
                // we have to assume it's missing if we see a getter as there
                // may be serious unintended side effects from calling a getter
                // that may be unpopulated. it might lazy load if it's an active
                // record, or it might throw an exception because the 'set' hasn't
                // been called.

                // check for isset as well as falsey for the situation where we're using
                // stdClasses as the response object
                if (isset($relation['getter']) || !isset($item->{$relationName}) || !$item->{$relationName}) {
                    $missing[$idx] = $item;
                }
            }

            if ($missing) {
                $result = $this->getRelated($missing, $relationName, ['stack'=>$stack], $meta);
                $relator = $this->getRelator($meta, $relationName);
                $this->populateObjectsWithRelated($missing, $result, $relation);
            }
        }
    }
426268
protected function insertBelongsTo(Model $record, Relation $relation, $columnValue)
    {
        $foreignKeyName = $relation->getForeignKey();
        $foreignKeyValue = $this->generateKey($columnValue);
        $record->$foreignKeyName = $foreignKeyValue;
    }
758404
protected function mergeWheresToHas(self $hasQuery, Relationship $relation)
    {
        // Here we have the "has" query and the original relation. We need to copy over any
        // where clauses the developer may have put in the relationship function over to
        // the has query, and then copy the bindings from the "has" query to the main.
        $relationQuery = $relation->getBaseQuery();

        $hasQuery->mergeWheres(
            $relationQuery->wheres,
            $relationQuery->getBindings()
        );

        $this->query->mergeBindings($hasQuery->getQuery());
    }
345372
public function add(Relations\Relation $relation)
    {
        if (isset($this->relations[$relation->getName()])) {
            throw new \Exception(
                'Relation '.$relation->getName().' is duplicated in model '.$relation->getParentModel()->getName()
            );
        }

        $this->relations[$relation->getName()] = $relation;
    }
901785
public function addCloneableRelation($relation) {
		$relations = $this->getCloneableRelations();
		if (in_array($relation, $relations)) return;
		$relations[] = $relation;
		$this->cloneable_relations = $relations;
	}
1196319
protected function updatePivotRelation(string $relation)
    {
        $hashes = $this->getEntityHashesFromRelation($relation);

        $cachedAttributes = $this->getCachedRawAttributes();

        if (array_key_exists($relation, $cachedAttributes)) {
            // Compare the two array of hashes to find out existing
            // pivot records, and the ones to be created.
            $new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
            $existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
        } else {
            $existing = [];
            $new = $hashes;
        }

        if (count($new) > 0) {
            $pivots = $this->getRelatedAggregatesFromHashes($new, $relation);

            $this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
        }

        if (count($existing) > 0) {
            foreach ($existing as $pivotHash) {
                $this->updatePivotIfDirty($pivotHash, $relation);
            }
        }
    }
254267
protected function duplicatePivotedRelation($relation, $relation_name, $clone) {

		// If duplicating between databases, do not duplicate relations. The related
		// instance may not exist in the other database or could have a different
		// primary key.
		if ($this->write_connection) return;

		// Loop trough current relations and attach to clone
		$relation->get()->each(function ($foreign) use ($clone, $relation_name) {
			$pivot_attributes = array_except($foreign->pivot->getAttributes(), [
				$foreign->pivot->getRelatedKey(),
				$foreign->pivot->getForeignKey(),
				$foreign->pivot->getCreatedAtColumn(),
				$foreign->pivot->getUpdatedAtColumn()
			]);
			$clone->$relation_name()->attach($foreign, $pivot_attributes);
		});
	}
1315654
private void copyRelations(CmsDbContext dbc, CmsResource source, CmsResource target) throws CmsException {

        // copy relations all relations
        CmsObject cms = new CmsObject(getSecurityManager(), dbc.getRequestContext());
        Iterator<CmsRelation> itRelations = getRelationsForResource(
            dbc,
            source,
            CmsRelationFilter.TARGETS.filterNotDefinedInContent()).iterator();
        while (itRelations.hasNext()) {
            CmsRelation relation = itRelations.next();
            if (relation.getType().getCopyBehavior() == CopyBehavior.copy) {
                try {
                    CmsResource relTarget = relation.getTarget(cms, CmsResourceFilter.ALL);
                    addRelationToResource(dbc, target, relTarget, relation.getType(), true);
                } catch (CmsVfsResourceNotFoundException e) {
                    // ignore this broken relation
                    if (LOG.isWarnEnabled()) {
                        LOG.warn(e.getLocalizedMessage(), e);
                    }
                }
            }
        }
        // repair categories
        repairCategories(dbc, getProjectIdForContext(dbc), target);
    }
1301234
public function assignChildren ($relationIdentifier, $id, $childIds, $subtractive = true)
		{
			$relation = $this->getRelation($relationIdentifier);
			$childIds = (array)$childIds;

			if (! $relation) {
				throw new RelationException('Invalid relation `' . $relationIdentifier);
			}

			if ($relation instanceof OneToMany) {
				$this->database->createQuery()
					->update()
					->table($relation->getRelatedTable())
					->set(
						array(
							$relation->getRelatedKey() => $id,
						)
					)
					->whereIn($this->database->primaryKey($relation->getRelatedTable()), $childIds)
					->execute();

				if ($subtractive) {
					$this->database->createQuery()
						->update()
						->table($relation->getRelatedTable())
						->set(
							array(
								$relation->getRelatedKey() => null,
							)
						)
						->whereEqual($relation->getRelatedKey(), $id)
						->whereNotIn($this->database->primaryKey($relation->getRelatedTable()), $childIds)
						->execute();
				}
			} else if ($relation instanceof ManyToMany) {
				if ($subtractive) {
					$this->database->createQuery()
						->delete()
						->from($relation->getRelationalTable())
						->whereEqual($relation->getKey(), $id)
						->whereNotIn($relation->getRelatedKey(), $childIds)
						->execute();
				}

				$query = $this->database->createQuery();

				$assignedIds =
					$query->select($relation->getRelatedKey())
						->from($relation->getRelationalTable())
						->whereEqual($relation->getKey(), $id)
						->execute()
						->fetchColumn();

				$newIds = array_diff($childIds, $assignedIds);

				foreach ($newIds as $newId) {
					$this->database->createQuery()
						->insert()
						->into($relation->getRelationalTable())
						->set(
							array(
								$relation->getKey()        => $id,
								$relation->getRelatedKey() => $newId,
							)
						)
						->execute();
				}
			} else {
				throw new RelationException('Invalid relation type `' . gettype($relation) . '`');
			}
		}