Make sure that pivot data in a valid format.

@param array $pivotData

@throws RelationException
327480
private function assertPivot(array $pivotData)
    {
        if ($diff = array_diff(array_keys($pivotData), $this->schema[Record::PIVOT_COLUMNS])) {
            throw new RelationException(
                "Invalid pivot data, undefined columns found: " . join(', ', $diff)
            );
        }
    }
786162
protected function validateData($data): void
    {
        if ($data instanceof FormInterface) {
            $data = $data->getData();
        }

        if (!\is_object($data)) {
            throw new UnexpectedTypeException($data, 'object');
        }
    }
924634
protected function newPivot($data = [], $isUpdate = false)
    {
        $class = $this->pivotName ?: '\\think\\model\\Pivot';
        $pivot = new $class($data, $this->parent, $this->middle);

        if ($pivot instanceof Pivot) {
            return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot;
        }

        throw new Exception('pivot model must extends: \think\model\Pivot');
    }
668073
public function queueUnresolvedObjects($data, $relations)
	{
		if( is_object($data) )
		{

			if( $data instanceof Model )
			{
				$data = $data->getData();
			}
			if( !$this->resolved($data) )
			{
				$this->addToQueue($data, $relations);
				return;
			}

			$data = get_object_vars($data);

		}

		if( is_array($data) )
		{
			foreach ($data as $key => $value)
			{
				if(is_int($key))
				{
					$this->queueUnresolvedObjects($value, $relations);
				}
				if($this->hasRelation($key, $relations))
				{
					$nestedRelations = $this->getNestedRelations($key, $relations);
					$this->queueUnresolvedObjects($value, $nestedRelations);
				}
				
			}
		}
	}
1262964
protected function setConnectedRelationOptions()
    {
        $pivotTable = $this->defaultOptions['pivot_table'];
        $foreignKey = $this->defaultOptions['foreign_key'];
        $relatedKey = $this->defaultOptions['related_key'];

        if (! $this->db->isTableExists($pivotTable)) {
            $question = "Can't find table {$this->text['pivot_table']} in the database as {$this->text['pivot_text']}, choice one!";
            $pivotTable = $this->options['pivot_table'] = $this->command->choice(
                $question, $this->getTables());

            $this->text['pivot_table'] = "[".$this->command->paintString($pivotTable, 'green')."]";
        }

        if (! $this->db->isFieldExists($pivotTable, $foreignKey)) {
            $question = "Can't find field {$this->text['foreign_key']} in the table {$this->text['pivot_table']} as {$this->text['foreign_text']} of table {$this->text['table']}, choice one!";
            $this->options['foreign_key'] = $this->command->choice($question, $this->getFields($pivotTable));
        }

        if (! $this->db->isFieldExists($pivotTable, $relatedKey)) {
            $question = "Can't find field {$this->text['related_key']} in the table {$this->text['pivot_table']} as {$this->text['related_text']} of table {$this->text['to_table']}, choice one!";
            $this->options['related_key'] = $this->command->choice($question, $this->getFields($pivotTable));
        }
    }
756356
protected function validateRelations(): void
    {
        $relations = $this->getArrayValue($this->joryArray, ['rlt', 'relations']);

        // No relations set, that's ok. return.
        if (! $relations) {
            return;
        }

        if (! is_array($relations)) {
            throw new JoryException('The relation parameter should be an array. (Location: '.$this->address.'relations)');
        }

        foreach ($relations as $name => $jory) {
            $this->validateRelation($name, $jory);
        }
    }
671274
public function detach(array $ids, BelongsToMany $relation)
    {
        if (empty($ids)) {
            return;
        }

        $this->newPivotQuery($relation)
             ->whereIn($this->getRelatedPivotKeyName($relation), $ids)
             ->delete();
    }
817408
public function attach($data, $pivot = [])
    {
        if (is_array($data)) {
            if (key($data) === 0) {
                $id = $data;
            } else {
                // 保存关联表数据
                $model = new $this->model;
                $id    = $model->insertGetId($data);
            }
        } elseif (is_numeric($data) || is_string($data)) {
            // 根据关联表主键直接写入中间表
            $id = $data;
        } elseif ($data instanceof Model) {
            // 根据关联表主键直接写入中间表
            $relationFk = $data->getPk();
            $id         = $data->$relationFk;
        }

        if ($id) {
            // 保存中间表数据
            $pk                     = $this->parent->getPk();
            $pivot[$this->localKey] = $this->parent->$pk;
            $ids                    = (array) $id;

            foreach ($ids as $id) {
                $pivot[$this->foreignKey] = $id;
                $this->pivot->replace()
                    ->exists(false)
                    ->save($pivot);
                $result[] = $this->newPivot($pivot, true);
            }

            if (count($result) == 1) {
                // 返回中间表模型对象
                $result = $result[0];
            }

            return $result;
        } else {
            throw new Exception('miss relation data');
        }
    }
682091
public function determineFormDisplayStrategy(ModelRelationData $data)
    {
        $type = null;

        switch ($data->type) {

            case RelationType::BELONGS_TO:
            case RelationType::BELONGS_TO_THROUGH:
            case RelationType::MORPH_ONE:
            case RelationType::HAS_ONE:
                $type = FormDisplayStrategy::RELATION_SINGLE_AUTOCOMPLETE;
                break;

            case RelationType::BELONGS_TO_MANY:
            case RelationType::HAS_MANY:
            case RelationType::MORPH_MANY:
            case RelationType::MORPH_TO_MANY:
            case RelationType::MORPHED_BY_MANY:
                $type = FormDisplayStrategy::RELATION_PLURAL_AUTOCOMPLETE;
                break;

            case RelationType::MORPH_TO:
                $type = FormDisplayStrategy::RELATION_SINGLE_MORPH_AUTOCOMPLETE;
                break;
        }

        return $type;
    }
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);
            }
        }
    }
1177051
protected function joinEagerLoadedColumn($relation, $relationColumn)
    {
        $table     = '';
        $deletedAt = false;
        $lastQuery = $this->query;
        foreach (explode('.', $relation) as $eachRelation) {
            $model = $lastQuery->getRelation($eachRelation);
            switch (true) {
                case $model instanceof BelongsToMany:
                    $pivot   = $model->getTable();
                    $pivotPK = $model->getExistenceCompareKey();
                    $pivotFK = $model->getQualifiedParentKeyName();
                    $this->performJoin($pivot, $pivotPK, $pivotFK);

                    $related = $model->getRelated();
                    $table   = $related->getTable();
                    $tablePK = $related->getForeignKey();
                    $foreign = $pivot . '.' . $tablePK;
                    $other   = $related->getQualifiedKeyName();

                    $lastQuery->addSelect($table . '.' . $relationColumn);
                    $this->performJoin($table, $foreign, $other);

                    break;

                case $model instanceof HasOneOrMany:
                    $table     = $model->getRelated()->getTable();
                    $foreign   = $model->getQualifiedForeignKeyName();
                    $other     = $model->getQualifiedParentKeyName();
                    $deletedAt = $this->checkSoftDeletesOnModel($model->getRelated());
                    break;

                case $model instanceof BelongsTo:
                    $table     = $model->getRelated()->getTable();
                    $foreign   = $model->getQualifiedForeignKeyName();
                    $other     = $model->getQualifiedOwnerKeyName();
                    $deletedAt = $this->checkSoftDeletesOnModel($model->getRelated());
                    break;

                default:
                    throw new Exception('Relation ' . get_class($model) . ' is not yet supported.');
            }
            $this->performJoin($table, $foreign, $other, $deletedAt);
            $lastQuery = $model->getQuery();
        }

        return $table . '.' . $relationColumn;
    }
28501
public function setRelated($value)
    {
        $this->loadData(true);

        if (is_null($value)) {
            $value = [];
        }

        if (!is_array($value)) {
            throw new RelationException("HasMany relation can only be set with array of entities");
        }

        //Do not add items twice
        $matched = [];
        foreach ($value as $index => $record) {
            if (is_null($record)) {
                unset($value[$index]);
                continue;
            }

            $this->assertValid($record);
            if (!empty($instance = $this->matchOne($record))) {
                $matched[] = $instance;
                unset($value[$index]);
            }
        }

        //Unlink records
        foreach (array_diff($this->instances, $matched) as $record) {
            $this->unlink($record);
        }

        //Add new record
        foreach ($value as $record) {
            $this->link($record);
        }
    }
1021828
protected function updateRelation($relationsData)
    {
        foreach ($relationsData as $name => $values) {
            if (!method_exists($this->model, $name)) {
                continue;
            }

            $relation = $this->model->$name();

            $oneToOneRelation = $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne
                || $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphOne;

            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);

            if (empty($prepared)) {
                continue;
            }

            switch (get_class($relation)) {
                case \Illuminate\Database\Eloquent\Relations\BelongsToMany::class:
                case \Illuminate\Database\Eloquent\Relations\MorphToMany::class:
                    if (isset($prepared[$name])) {
                        $relation->sync($prepared[$name]);
                    }
                    break;
                case \Illuminate\Database\Eloquent\Relations\HasOne::class:

                    $related = $this->model->$name;

                    // if related is empty
                    if (is_null($related)) {
                        $related = $relation->getRelated();
                        $related->{$relation->getForeignKeyName()} = $this->model->{$this->model->getKeyName()};
                    }

                    foreach ($prepared[$name] as $column => $value) {
                        $related->setAttribute($column, $value);
                    }

                    $related->save();
                    break;
                case \Illuminate\Database\Eloquent\Relations\MorphOne::class:
                    $related = $this->model->$name;
                    if (is_null($related)) {
                        $related = $relation->make();
                    }
                    foreach ($prepared[$name] as $column => $value) {
                        $related->setAttribute($column, $value);
                    }
                    $related->save();
                    break;
                case \Illuminate\Database\Eloquent\Relations\HasMany::class:
                case \Illuminate\Database\Eloquent\Relations\MorphMany::class:

                    foreach ($prepared[$name] as $related) {
                        $relation = $this->model()->$name();

                        $keyName = $relation->getRelated()->getKeyName();

                        $instance = $relation->findOrNew(array_get($related, $keyName));

                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
                            $instance->delete();

                            continue;
                        }

                        array_forget($related, static::REMOVE_FLAG_NAME);

                        $instance->fill($related);

                        $instance->save();
                    }

                    break;
            }
        }
    }
429607
protected function validateData()
    {
        foreach ($this->relationalData as $attribute => &$data) {
            /** @var ActiveRecord $model */
            /** @var ActiveQuery $activeQuery */
            $activeQuery = $data['activeQuery'];
            foreach ($data['newModels'] as &$model) {
                if (!$model->validate()) {
                    $_errors = $model->getErrors();
                    $errors = [];

                    foreach ($_errors as $relatedAttribute => $error) {
                        if (!$activeQuery->multiple || !isset($activeQuery->link[$relatedAttribute])) {
                            $errors[$relatedAttribute] = $error;
                        }
                    }

                    if (count($errors)) {
                        $this->owner->addError($attribute, $errors);

                        return false;
                    }
                }
            }
        }

        return true;
    }
1227423
protected function updatePivotIfDirty(string $pivotHash, string $relation)
    {
        $aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);

        if ($aggregate->hasAttribute('pivot')) {
            $pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();

            $cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);

            $actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));

            $dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);

            if (count($dirty) > 0) {
                $id = $aggregate->getEntityKeyValue();

                $this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
            }
        }
    }
1105557
protected function updateRelation($eloquentModel, $name, $relationMapping, $action)
    {
        // set foreign key for belongsTo/morphTo relation
        if ($relationMapping['type'] == 'belongsTo' || $relationMapping['type'] == 'morphTo') {
            $this->updateBelongsToRelation($eloquentModel, $name, $action);
        }

        // set foreign keys for belongsToMany/morphToMany relation
        if (($relationMapping['type'] == 'belongsToMany' || $relationMapping['type'] == 'morphToMany') && ! $relationMapping['inverse']) {
            $this->updateBelongsToManyRelation($eloquentModel, $name, $action);
        }
    }
1227986
protected function addPivotRelation(string $relation)
    {
        if (!in_array($relation, $this->pivotRelations)) {
            $this->pivotRelations[] = $relation;
        }
    }