解除关联的一个中间表数据
@access public
@param integer|array $data        数据 可以使用关联对象的主键
@param bool          $relationDel 是否同时删除关联表数据
@return integer
189010
public function detach($data = null, $relationDel = false)
    {
        if (is_array($data)) {
            $id = $data;
        } elseif (is_numeric($data) || is_string($data)) {
            // 根据关联表主键直接写入中间表
            $id = $data;
        } elseif ($data instanceof Model) {
            // 根据关联表主键直接写入中间表
            $relationFk = $data->getPk();
            $id         = $data->$relationFk;
        }

        // 删除中间表数据
        $pk      = $this->parent->getPk();
        $pivot[] = [$this->localKey, '=', $this->parent->$pk];

        if (isset($id)) {
            $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id];
        }

        $result = $this->pivot->where($pivot)->delete();

        // 删除关联表数据
        if (isset($id) && $relationDel) {
            $model = $this->model;
            $model::destroy($id);
        }

        return $result;
    }
504859
protected function joinEagerLoadedColumn($relation, $relationColumn)
    {
        $joins = [];
        foreach ((array) $this->getQueryBuilder()->joins as $key => $join) {
            $joins[] = $join->table;
        }

        $model = $this->query->getRelation($relation);
        if ($model instanceof BelongsToMany) {
            $pivot   = $model->getTable();
            $pivotPK = $model->getForeignKey();
            $pivotFK = $model->getQualifiedParentKeyName();

            if (!in_array($pivot, $joins)) {
                $this->getQueryBuilder()->leftJoin($pivot, $pivotPK, '=', $pivotFK);
            }

            $related = $model->getRelated();
            $table   = $related->getTable();
            $tablePK = $related->getForeignKey();
            $tableFK = $related->getQualifiedKeyName();

            if (!in_array($table, $joins)) {
                $this->getQueryBuilder()->leftJoin($table, $pivot . '.' . $tablePK, '=', $tableFK);
            }
        } else {
            $table   = $model->getRelated()->getTable();
            $foreign = $model->getQualifiedForeignKey();
            $other   = $model->getQualifiedOtherKeyName();
            if (!in_array($table, $joins)) {
                $this->getQueryBuilder()->leftJoin($table, $foreign, '=', $other);
            }
        }

        $column = $table . '.' . $relationColumn;

        return $column;
    }
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) . '`');
			}
		}
118923
protected function getRelationData(Relation $modelRelation)
    {
        if ($this->parent && !$modelRelation->isSelfRelation() && get_class($this->parent) == get_class($modelRelation->getModel())) {
            $value = $this->parent;
        } else {
            // 获取关联数据
            $value = $modelRelation->getRelation();
        }

        return $value;
    }
151230
public function getRelatedErrors($relationName = null)
    {
        if ($relationName === null) {
            return $this->_relatedErrors;
        } else {
            return isset($this->_relatedErrors[$relationName]) ? $this->_relatedErrors[$relationName] : [];
        }
    }
67385
private function _loadOrCreateRelationModel($data, $fks, $modelClass, $relationName)
    {

        /** @var BaseActiveRecord $relationModel */
        $relationModel = null;
        if (!empty($fks)) {
            $relationModel = $modelClass::findOne($fks);
        }
        if (!($relationModel instanceof BaseActiveRecord) && !empty($data)) {
            $relationModel = new $modelClass;
        }
        // If a custom scenario is set, apply it here to correctly be able to set the model attributes
        if (array_key_exists($relationName, $this->_relationsScenario)) {
            $relationModel->setScenario($this->_relationsScenario[$relationName]);
        }
        if (($relationModel instanceof BaseActiveRecord) && is_array($data)) {
            $relationModel->setAttributes($data);
            if ($relationModel->hasMethod('loadRelations')) {
                $relationModel->loadRelations($data);
            }

        }
        return $relationModel;
    }
355714
protected function deleteModels($attribute)
    {
        $data = $this->relationalData[$attribute];

        /** @var ActiveRecord $model */
        foreach ($data['oldModels'] as $model) {
            if ($this->isDeletedModel($model, $attribute)) {
                if (!$model->delete()) {
                    Yii::$app->getDb()->getTransaction()->rollBack();
                    throw new RelationException('Model ' . $model::className() . ' not deleted due to unknown error');
                }
            }
        }

        $this->relationsMap($data['oldRows'], function($row) use ($attribute, $data) {
            if ($this->isDeletedRow($row, $attribute)) {
                Yii::$app->db->createCommand()
                    ->delete($data['junctionTable'], $row)
                    ->execute();
            }
        });
    }
513197
public function withCount($relation, $subQuery = true)
    {
        if (!$subQuery) {
            $this->options['with_count'] = $relation;
        } else {
            $relations = is_string($relation) ? explode(',', $relation) : $relation;
            if (!isset($this->options['field'])) {
                $this->field('*');
            }
            foreach ($relations as $key => $relation) {
                $closure = false;
                if ($relation instanceof \Closure) {
                    $closure  = $relation;
                    $relation = $key;
                }
                $relation = Parse::parseName($relation, 1, false);
                $count    = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')';
                $this->field([$count => Parse::parseName($relation) . '_count']);
            }
        }
        return $this;
    }
5672
public function withCount( $relations )
	{
		if ( is_null( $this->query->columns ) )
		{
			$this->query->select( ['*'] );
		}

		$relations = is_array( $relations ) ? $relations : func_get_args();

		foreach ( $this->parseWithRelations( $relations ) as $name => $constraints )
		{
			// Here we will get the relationship count query and prepare to add it to the main query
			// as a sub-select. First, we'll get the "has" query and use that to get the relation
			// count query. We will normalize the relation name then append _count as the name.
			$relation = $this->getHasRelationQuery( $name );

			$query = $relation->getRelationCountQuery( $relation->getRelated()->newQuery(), $this );

			$query->callScope( $constraints );

			$query->mergeModelDefinedRelationConstraints( $relation->getQuery() );

			$this->selectSub( $query->toBase(), snake_case( $name ) . '_count' );
		}

		return $this;
	}
793763
public function getCursor($sql, $bind = [], $master = false, $model = null, $condition = null, $relation = null)
    {
        $this->initConnect($master);

        // 记录SQL语句
        $this->queryStr = $sql;

        $this->bind = $bind;

        Db::$queryTimes++;

        // 调试开始
        $this->debug(true);

        // 预处理
        $this->PDOStatement = $this->linkID->prepare($sql);

        // 是否为存储过程调用
        $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);

        // 参数绑定
        if ($procedure) {
            $this->bindParam($bind);
        } else {
            $this->bindValue($bind);
        }

        // 执行查询
        $this->PDOStatement->execute();

        // 调试结束
        $this->debug(false, '', $master);

        // 返回结果集
        while ($result = $this->PDOStatement->fetch($this->fetchType)) {
            if ($model) {
                $instance = $model->newInstance($result, $condition);

                if ($relation) {
                    $instance->relationQuery($relation);
                }

                yield $instance;
            } else {
                yield $result;
            }
        }
    }
850815
public function getRelation($relationName) {
        $relationInstance = null;
        if (isset($this->relations[$relationName])) {
            $relationInstance = $this->relations[$relationName];
        }
        return $relationInstance;
    }
1113277
public function related($relation, $column, $operator = null, $value = null, $boolean = 'and')
    {
        if ($column instanceof \Closure) {
            return $this->addRelated($relation, $column);
        }

        // If there is no value it is a where = ? query and we set the appropriate params
        if ($value === null) {
            $value = $operator;
            $operator = '=';
        }

        return $this->addRelated($relation, function ($query) use ($column, $operator, $value, $boolean) {
            return $query->where($column, $operator, $value, $boolean);
        });
    }
840613
public function getRelationQuery(array $data) {
        foreach ($this->links as $key => $val) {
            $this->query->whereIn(
                $val, $this->getKeys($data, $key)
            );
        }
        return $this->query;
    }
826948
public function lazyLoad($instance, $relation_name)
    {
        if (($relation = $this->get($instance, $relation_name)) === false) {
            throw new InvalidValueException($relation);
        }

        $type = $relation->type;
        $referenceModel = $relation->referenceModel;
        $valueField = $relation->valueField;
        if ($type === Relation::TYPE_HAS_ONE) {
            return $referenceModel::query()->whereEq($relation->keyField, $instance->$valueField)->setFetchType(false);
        } elseif ($type === Relation::TYPE_BELONGS_TO) {
            return $referenceModel::query()->whereEq($relation->keyField, $instance->$valueField)->setFetchType(false);
        } elseif ($type === Relation::TYPE_HAS_MANY) {
            return $referenceModel::query()->whereEq($relation->keyField, $instance->$valueField)->setFetchType(true);
        } elseif ($type === Relation::TYPE_HAS_MANY_TO_MANY) {
            $ids = $instance::values($relation->keyField, [$valueField => $instance->$valueField]);
            /** @var \ManaPHP\Model $referenceInstance */
            $referenceInstance = is_string($referenceModel) ? new $referenceModel : $referenceModel;
            return $referenceModel::query()->whereIn($referenceInstance->getPrimaryKey(), $ids)->setFetchType(true);
        } elseif ($type === Relation::TYPE_HAS_MANY_VIA) {
            $via = $relation->keyField;
            /** @var \ManaPHP\Model $reference */
            $reference = new $referenceModel();
            $ids = $via::values($reference->getPrimaryKey(), [$valueField => $instance->$valueField]);
            return $referenceModel::query()->whereIn($reference->getPrimaryKey(), $ids)->setFetchType(true);
        } else {
            throw  new NotSupportedException(['unknown relation type: :type', 'type' => $type]);
        }
    }
1284403
private function populateRelation(array $data, $object, ?string $format, array $context, array $components, string $type): array
    {
        $class = $this->getObjectClass($object);

        $attributesMetadata = \array_key_exists($class, $this->attributesMetadataCache) ?
            $this->attributesMetadataCache[$class] :
            $this->attributesMetadataCache[$class] = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;

        $key = '_'.$type;
        foreach ($components[$type] as $relation) {
            if (null !== $attributesMetadata && $this->isMaxDepthReached($attributesMetadata, $class, $relation['name'], $context)) {
                continue;
            }

            $attributeValue = $this->getAttributeValue($object, $relation['name'], $format, $context);
            if (empty($attributeValue)) {
                continue;
            }

            $relationName = $relation['name'];
            if ($this->nameConverter) {
                $relationName = $this->nameConverter->normalize($relationName, $class, $format, $context);
            }

            if ('one' === $relation['cardinality']) {
                if ('links' === $type) {
                    $data[$key][$relationName]['href'] = $this->getRelationIri($attributeValue);
                    continue;
                }

                $data[$key][$relationName] = $attributeValue;
                continue;
            }

            // many
            $data[$key][$relationName] = [];
            foreach ($attributeValue as $rel) {
                if ('links' === $type) {
                    $rel = ['href' => $this->getRelationIri($rel)];
                }

                $data[$key][$relationName][] = $rel;
            }
        }

        return $data;
    }
698533
protected function getCount(Relation $relation)
    {
        if ( ! $relation) return 0;

        $query = $this->modifyRelationQueryForContext($relation->getRelated(), $relation->getQuery());

        return $query->count();
    }
942930
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 => $item) {
            if (is_null($item)) {
                unset($value[$index]);
                continue;
            }

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

        $this->deleteInstances = array_diff($this->instances, $matched);
        $this->instances = $matched + $value;
    }