预载入关联查询（数据集）
@access public
@param  array    $resultSet   数据集
@param  string   $relation    当前关联名
@param  string   $subRelation 子关联名
@param  \Closure $closure     闭包
@return void
176321
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
    {
        $localKey   = $this->localKey;
        $foreignKey = $this->foreignKey;

        $pk    = $resultSet[0]->getPk();
        $range = [];
        foreach ($resultSet as $result) {
            // 获取关联外键列表
            if (isset($result->$pk)) {
                $range[] = $result->$pk;
            }
        }

        if (!empty($range)) {
            // 查询关联数据
            $data = $this->eagerlyManyToMany([
                ['pivot.' . $localKey, 'in', $range],
            ], $relation, $subRelation, $closure);

            // 关联属性名
            $attr = Db::parseName($relation);

            // 关联数据封装
            foreach ($resultSet as $result) {
                if (!isset($data[$result->$pk])) {
                    $data[$result->$pk] = [];
                }

                $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
            }
        }
    }
518242
protected function cacheManyRelationResults(string $parentKey, string $relation, $results, Relationship $relationship)
    {
        $this->cache[$parentKey][$relation] = [];

        foreach ($results as $result) {
            $cachedRelationship = $this->getCachedRelationship($relation, $result, $relationship);

            $relatedHash = $cachedRelationship->getHash();

            $this->cache[$parentKey][$relation][$relatedHash] = $cachedRelationship;
        }
    }
506392
public function use_subquery()
	{
		return ( ! empty($this->relations) and ( ! empty($this->limit) or ! empty($this->offset)));
	}
131317
private function mapGroup($linkTableName, $relation, $fields = '*', $where = [], $orderBy = ''): Result
    {
        // 生成关联表模型对象
        $table = $this->makeTable($linkTableName);

        // 生成本表关联字段与关联表关联字段
        list ($field, $linkField) = $this->relation($relation);

        // 生成本表关联字段的所有值
        $keys = $this->getMapKeys($field);
        if (!$keys) {
            return $this;
        }

        $linkResult = [];
        foreach ($keys as $value) {
            $where[$linkField] = $value;
            $linkData = $table->groupBy($linkField)->select($this->append($fields, $linkField), $where, $orderBy)
                ->toArray();

            //如果没有关联数据
            if (!$linkData) {
                continue;
            }

            $key = $linkData[0][$linkField];
            if (!array_key_exists($key, $linkResult)) {
                $linkResult[$key] = [];
            }
            $linkResult[$key] = $linkData[0];
        }

        $fields = $this->table->createFields($fields);

        // 为本表每行数据补充字段
        foreach ($this->data as $key => $row) {
            // 字段补充成功
            if ($this->mapFill($linkResult, $field, $row, $key)) {
                continue;
            }

            // 没有对应成功,补充空字符串
            $this->data[$key] = $this->appendNull($row, $fields);
        }
        $this->row2Obj();
        return $this;
    }
715301
public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null)
    {
        if (strpos($relation, '.') !== false) {
            return $this->hasNested($relation, $operator, $count, $boolean, $callback);
        }

        $relation = $this->getRelationWithoutConstraints($relation);

        if ($relation instanceof MorphTo) {
            throw new RuntimeException('has() and whereHas() do not support MorphTo relationships.');
        }

        // If we only need to check for the existence of the relation, then we can optimize
        // the subquery to only run a "where exists" clause instead of this full "count"
        // clause. This will make these queries run much faster compared with a count.
        $method = $this->canUseExistsForExistenceCheck($operator, $count)
                        ? 'getRelationExistenceQuery'
                        : 'getRelationExistenceCountQuery';

        $hasQuery = $relation->{$method}(
            $relation->getRelated()->newQueryWithoutRelationships(), $this
        );

        // Next we will call any given callback as an "anonymous" scope so they can get the
        // proper logical grouping of the where clauses if needed by this Eloquent query
        // builder. Then, we will be ready to finalize and return this query instance.
        if ($callback) {
            $hasQuery->callScope($callback);
        }

        return $this->addHasWhere(
            $hasQuery, $relation, $operator, $count, $boolean
        );
    }
1241827
public function getRelationCountQuery($closure)
    {
        if ($closure) {
            $closure($this->query);
        }

        return $this->query
            ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk())
            ->fetchSql()
            ->count();
    }
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) . '`');
			}
		}
364187
public function eagerly(Query $query, $relation, $field, $joinType, $closure, $first)
    {
        $name = Db::parseName(basename(str_replace('\\', '/', get_class($this->parent))));

        if ($first) {
            $table = $query->getTable();
            $query->table([$table => $name]);

            if ($query->getOptions('field')) {
                $masterField = $query->getOptions('field');
                $query->removeOption('field');
            } else {
                $masterField = true;
            }

            $query->field($masterField, false, $table, $name);
        }

        // 预载入封装
        $joinTable = $this->query->getTable();
        $joinAlias = $relation;
        $joinType  = $joinType ?: $this->joinType;

        $query->via($joinAlias);

        if ($this instanceof BelongsTo) {
            $joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey;
        } else {
            $joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey;
        }

        if ($closure) {
            // 执行闭包查询
            $closure($query);
            // 使用withField指定获取关联的字段，如
            // $query->where(['id'=>1])->withField('id,name');
            if ($query->getOptions('with_field')) {
                $field = $query->getOptions('with_field');
                $query->removeOption('with_field');
            }
        }

        $query->join([$joinTable => $joinAlias], $joinOn, $joinType)
            ->field($field, false, $joinTable, $joinAlias, $relation . '__');
    }
22681
protected function resultSetToModelCollection(array $resultSet)
    {
        if (!empty($this->options['collection']) && is_string($this->options['collection'])) {
            $collection = $this->options['collection'];
        }

        if (empty($resultSet)) {
            return $this->model->toCollection([], isset($collection) ? $collection : null);
        }

        // 检查动态获取器
        if (!empty($this->options['with_attr'])) {
            foreach ($this->options['with_attr'] as $name => $val) {
                if (strpos($name, '.')) {
                    list($relation, $field) = explode('.', $name);

                    $withRelationAttr[$relation][$field] = $val;
                    unset($this->options['with_attr'][$name]);
                }
            }
        }

        $withRelationAttr = isset($withRelationAttr) ? $withRelationAttr : [];

        foreach ($resultSet as $key => &$result) {
            // 数据转换为模型对象
            $this->resultToModel($result, $this->options, true, $withRelationAttr);
        }

        if (!empty($this->options['with'])) {
            // 预载入
            $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr);
        }

        if (!empty($this->options['with_join'])) {
            // JOIN预载入
            $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true);
        }

        // 模型数据集转换
        return $result->toCollection($resultSet, isset($collection) ? $collection : null);
    }
595982
public function getRelationCountQuery($closure)
    {
        if ($closure) {
            $closure($this->query);
        }

        return $this->query
            ->where([
                [$this->morphKey, 'exp', Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())],
                [$this->morphType, '=', $this->type],
            ])
            ->fetchSql()
            ->count();
    }
1184169
protected function buildRelationsFromModels(string $relationName, Closure $relationConstraints): Collection
    {
        return $this->models->toBase()->map(
            function (Model $model) use ($relationName, $relationConstraints) {
                $relation = $this->getRelationInstance($relationName);

                $relation->addEagerConstraints([$model]);

                // Call the constraints
                $relationConstraints($relation, $model);

                if (method_exists($relation, 'shouldSelect')) {
                    $shouldSelect = new ReflectionMethod(get_class($relation), 'shouldSelect');
                    $shouldSelect->setAccessible(true);
                    $select = $shouldSelect->invoke($relation, ['*']);

                    $relation->addSelect($select);
                } elseif (method_exists($relation, 'getSelectColumns')) {
                    $getSelectColumns = new ReflectionMethod(get_class($relation), 'getSelectColumns');
                    $getSelectColumns->setAccessible(true);
                    $select = $getSelectColumns->invoke($relation, ['*']);

                    $relation->addSelect($select);
                }

                $relation->initRelation([$model], $relationName);

                return $relation;
            }
        );
    }
1368296
public function collection_assign( $relation, &$collection, $callback = null ) 
	{
		// make the query
		$this->collection_query( $collection );
		
		call_user_func_array( $callback, array( &$this->query ) );
		
		// get the reults
		$results = $this->query->run();
		
		foreach( $collection as $item )
		{
			if ( $this->singleton )
			{
				$item->raw_set( $relation, reset( $results[ $item->raw( $this->local_key ) ] ) );
			}
			else
			{
				$item->raw_set( $relation, $results[ $item->raw( $this->local_key ) ] );
			}
		}
	}
808008
protected function withAggregate($relation, $aggregate = 'count', $field = '*', $subQuery = true)
    {
        $relations = is_string($relation) ? explode(',', $relation) : $relation;

        if (!$subQuery) {
            $this->options['with_count'][] = [$relations, $aggregate, $field];
        } else {
            if (!isset($this->options['field'])) {
                $this->field('*');
            }

            foreach ($relations as $key => $relation) {
                $closure = null;
                if ($relation instanceof \Closure) {
                    $closure  = $relation;
                    $relation = $key;
                } elseif (!is_int($key)) {
                    $aggregateField = $relation;
                    $relation       = $key;
                }

                if (!isset($aggregateField)) {
                    $aggregateField = Db::parseName($relation) . '_' . $aggregate;
                }

                $relation = Db::parseName($relation, 1, false);
                $count    = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field) . ')';

                $this->field([$count => $aggregateField]);
            }
        }

        return $this;
    }
1220262
public function relationCount($result, $closure)
    {
        $pk    = $result->getPk();
        $count = 0;

        if (isset($result->$pk)) {
            if ($closure) {
                $closure($this->query);
            }

            $count = $this->query
                ->where([
                    [$this->morphKey, '=', $result->$pk],
                    [$this->morphType, '=', $this->type],
                ])
                ->count();
        }

        return $count;
    }
187590
protected function cacheSingleRelationResult(string $parentKey, string $relation, $result, Relationship $relationship)
    {
        $this->cache[$parentKey][$relation] = $this->getCachedRelationship($relation, $result, $relationship);
    }
1118185
public function map($linkTableName, $relation, $fields = '*', $where = []): Row
    {
        // 生成关联表模型对象
        $table = $this->makeTable($linkTableName);

        // 生成本表关联字段与关联表关联字段
        list ($field, $linkField) = $this->relation($relation);

        // 如果本表关联字段不存在
        if (!array_key_exists($field, $this->data)) {
            return $this;
        }

        // 生成本表关联字段的所有值
        $key = $this->data[$field];

        if (is_null($key)) {
            $linkRow = false;
        } else {
            // 生成关联表查询条件
            $where[$linkField] = $key;

            // 关联表查询结果
            $linkRow = $table->row($this->append($fields, $linkField), $where);
        }

        // 如果未能对应
        if (!$linkRow) {
            $fields = $this->table->createFields($fields);
            $this->data = $this->appendNull($this->data, $fields);
            return $this;
        }

        // 这里是对应的数据
        $linkRow = $linkRow->toArray();

        // 处理每一个关联的字段
        foreach ($linkRow as $k => $v) {
            // 如果本表已经有此字段,则不扩展
            if (!array_key_exists($k, $this->data)) {
                $this->data[$k] = $v;
            }
        }

        return $this;
    }
982527
protected function joinForSearch($mappings, $subquery)
    {
        $mappings = is_array($mappings) ? $mappings : (array) $mappings;

        $columns = new ColumnCollection;

        $grammar = $this->query->getGrammar();

        $joiner = static::$joinerFactory->make($subquery->getQuery(), $this->model);

        // Here we loop through the search mappings in order to join related tables
        // appropriately and build a searchable column collection, which we will
        // use to build select and where clauses with correct table prefixes.
        foreach ($mappings as $mapping => $weight) {
            if (strpos($mapping, '.') !== false) {
                list($relation, $column) = $this->model->parseMappedColumn($mapping);

                $related = $joiner->leftJoin($relation);

                $columns->add(
                    new Column($grammar, $related->getTable(), $column, $mapping, $weight)
                );
            } else {
                $columns->add(
                    new Column($grammar, $this->model->getTable(), $mapping, $mapping, $weight)
                );
            }
        }

        return $columns;
    }