预载入关联查询（单个数据）
@access public
@param  Model    $result      数据对象
@param  string   $relation    当前关联名
@param  string   $subRelation 子关联名
@param  \Closure $closure     闭包
@return void
19982
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
    {
        $pk = $result->getPk();

        if (isset($result->$pk)) {
            $pk = $result->$pk;
            // 查询管理数据
            $data = $this->eagerlyManyToMany([
                ['pivot.' . $this->localKey, '=', $pk],
            ], $relation, $subRelation, $closure);

            // 关联数据封装
            if (!isset($data[$pk])) {
                $data[$pk] = [];
            }

            $result->setRelation(Db::parseName($relation), $this->resultSetBuild($data[$pk]));
        }
    }
911460
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
    {
        $morphKey  = $this->morphKey;
        $morphType = $this->morphType;
        $range     = [];

        foreach ($resultSet as $result) {
            // 获取关联外键列表
            if (!empty($result->$morphKey)) {
                $range[$result->$morphType][] = $result->$morphKey;
            }
        }

        if (!empty($range)) {
            // 关联属性名
            $attr = Db::parseName($relation);

            foreach ($range as $key => $val) {
                // 多态类型映射
                $model = $this->parseModel($key);
                $obj   = new $model;
                $pk    = $obj->getPk();
                $list  = $obj->all($val, $subRelation);
                $data  = [];

                foreach ($list as $k => $vo) {
                    $data[$vo->$pk] = $vo;
                }

                foreach ($resultSet as $result) {
                    if ($key == $result->$morphType) {
                        // 关联模型
                        if (!isset($data[$result->$morphKey])) {
                            $relationModel = null;
                        } else {
                            $relationModel = $data[$result->$morphKey];
                            $relationModel->setParent(clone $result);
                            $relationModel->isUpdate(true);
                        }

                        $result->setRelation($attr, $relationModel);
                    }
                }
            }
        }
    }
1062988
public function attach($model, string $relation, Model $value)
    {
        if (! $model instanceof Model) {
            $model = $this->find($model);
        }

        $model->$relation()->attach($value);
    }
791528
protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = null)
    {
        // 预载入关联查询 支持嵌套预载入
        if ($closure) {
            $closure($this->query);
        }

        $list     = $this->query->where($where)->with($subRelation)->select();
        $morphKey = $this->morphKey;

        // 组装模型数据
        $data = [];

        foreach ($list as $set) {
            $data[$set->$morphKey] = $set;
        }

        return $data;
    }
584916
public function relationQuery($relations, $withRelationAttr = [])
    {
        if (is_string($relations)) {
            $relations = explode(',', $relations);
        }

        foreach ($relations as $key => $relation) {
            $subRelation = '';
            $closure     = null;

            if ($relation instanceof \Closure) {
                // 支持闭包查询过滤关联条件
                $closure  = $relation;
                $relation = $key;
            }

            if (is_array($relation)) {
                $subRelation = $relation;
                $relation    = $key;
            } elseif (strpos($relation, '.')) {
                list($relation, $subRelation) = explode('.', $relation, 2);
            }

            $method       = Db::parseName($relation, 1, false);
            $relationName = Db::parseName($relation);

            $relationResult = $this->$method();

            if (isset($withRelationAttr[$relationName])) {
                $relationResult->withAttr($withRelationAttr[$relationName]);
            }

            $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure);

        }

        return $this;
    }
964095
public function addRelation(Relation $relation)
    {
        switch (get_class($relation)) {
            case BelongsTo::class:
                $this->relations['belongsTo'][] = $relation;
                break;

            case BelongsToMany::class:
                $this->relations['belongsToMany'][] = $relation;
                break;

            case HasOne::class:
                $this->relations['hasOne'][] = $relation;
                break;

            case HasMany::class:
                $this->relations['hasMany'][] = $relation;
                break;

            case HasManyThrough::class:
                $this->relations['hasManyThrough'][] = $relation;
                break;

            default:
                throw new \Exception("Unknown relation type " . get_class($relation));
        }
    }
1057476
public function findRelation($entity, $relationName, $orderColumn = null, $orderType = null) {
        $this->load();
        $mapper = MapperBuilder::buildFromClassName($this->mapping, get_class($entity));
        $relation = $mapper->getRelation($relationName);
        $relationMapper = MapperBuilder::buildFromName($this->mapping, $relation->getEntity());

        $selectQuery = "SELECT tmain.* FROM `" . $relationMapper->getTable() . "` tmain ";
        $joinQuery = QueryBuilder::buildJoinRelationQuery($relation, "tmain", "j1");
        $whereQuery = "WHERE j1." . $relation->getLocalColumn() . " = '" . $entity->getId() . "'";

        $orderQuery = "";
        if (!is_null($orderColumn)) {
            $orderQuery .= "ORDER BY $orderColumn ";
            if (!is_null($orderType)) {
                $orderQuery .= "$orderType";
            } else {
                $orderQuery .= "ASC";
            }
        }

        $query = $selectQuery . $joinQuery . $whereQuery . $orderQuery;
        $queryResult = $this->getDataSource()->query($query);
        if ($queryResult) {
            $collection = new Collection($relationMapper->getClass(), $queryResult, $relationMapper);
        } else {
            $collection = new Collection($relationMapper->getClass(), array(), $relationMapper);
        }

        return $collection;
    }
693153
protected function getCachedRelationship(string $relation, $result, Relationship $relationship)
    {
        $pivotColumns = $relationship->getPivotAttributes();

        if (!array_key_exists($relation, $this->pivotAttributes)) {
            $this->pivotAttributes[$relation] = $pivotColumns;
        }

        $wrapper = $this->factory->make($result);

        $hash = $wrapper->getEntityHash();

        if (count($pivotColumns) > 0) {
            $pivotAttributes = [];
            foreach ($pivotColumns as $column) {
                $pivot = $wrapper->getEntityAttribute('pivot');

                $pivotWrapper = $this->factory->make($pivot);

                $pivotAttributes[$column] = $pivotWrapper->getEntityAttribute($column);
            }

            $cachedRelationship = new CachedRelationship($hash, $pivotAttributes);
        } else {
            $cachedRelationship = new CachedRelationship($hash);
        }

        return $cachedRelationship;
    }
920437
private function oneToOne(&$structure, $property, $relation)
    {
        $from = $structure->name;
        $to = $relation->targetEntity;

        $reflection = null;
        try {
            $reflection = new \ReflectionClass($to);
        }catch(\Exception $e) { // @codeCoverageIgnore
            // Ignore, we will throw error on the next if.
        }
        if ($reflection === null || ! $reflection->isSubclassOf(Entity::class)) {
            throw new RelationException("The target entity of your relation on the entity '".$from."' and property '".$property->getName()."' has an unknown target Entity!"); // @codeCoverageIgnore
        }

        // Get join and set the join into the relation
        $join = $this->getJoin($property);
        $relation->join = $join;

        // Add declaration to the structure
        $structure->relationProperties[] = $property->getName();
        $structure->foreignColumnNames[] = $join->column;
        $structure->relations[$property->getName()] = $relation;
    }
1298592
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $join = false)
    {
        if ($join || 0 == $this->eagerlyType) {
            // 模型JOIN关联组装
            foreach ($resultSet as $result) {
                $this->match($this->model, $relation, $result);
            }
        } else {
            // IN查询
            $this->eagerlySet($resultSet, $relation, $subRelation, $closure);
        }
    }
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]));
            }
        }
    }
724310
public function whereHas($relation, $closure)
    {
        $this->model = $this->model->whereHas($relation, $closure);

        return $this;
    }
516198
public function getRelation($subRelation = '', $closure = null)
    {
        if ($closure) {
            $closure($this->query);
        }

        $this->baseQuery();

        $list   = $this->query->relation($subRelation)->select();
        $parent = clone $this->parent;

        foreach ($list as &$model) {
            $model->setParent($parent);
        }

        return $list;
    }
1235000
public function eagerlyResult(&$result, $relation, $subRelation, $closure, $join = false)
    {
        if (0 == $this->eagerlyType || $join) {
            // 模型JOIN关联组装
            $this->match($this->model, $relation, $result);
        } else {
            // IN查询
            $this->eagerlyOne($result, $relation, $subRelation, $closure);
        }
    }
182186
protected function resultToModel(&$result, $options = [], $resultSet = false, $withRelationAttr = [])
    {
        // 动态获取器
        if (!empty($options['with_attr']) && empty($withRelationAttr)) {
            foreach ($options['with_attr'] as $name => $val) {
                if (strpos($name, '.')) {
                    list($relation, $field) = explode('.', $name);

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

        $condition = (!$resultSet && isset($options['where']['$and'])) ? $options['where']['$and'] : null;
        $result    = $this->model->newInstance($result, $condition);

        // 动态获取器
        if (!empty($options['with_attr'])) {
            $result->setModelAttrs($options['with_attr']);
        }

        // 关联查询
        if (!empty($options['relation'])) {
            $result->relationQuery($options['relation'], $withRelationAttr);
        }

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

        // 关联统计
        if (!empty($options['with_count'])) {
            foreach ($options['with_count'] as $val) {
                $result->relationCount($result, $val[0], $val[1], $val[2]);
            }
        }

    }
95082
public function getRelation($subRelation = '', $closure = null)
    {
        if ($closure) {
            $closure($this->query);
        }

        $this->baseQuery();

        return $this->query->relation($subRelation)->select();
    }
187590
protected function cacheSingleRelationResult(string $parentKey, string $relation, $result, Relationship $relationship)
    {
        $this->cache[$parentKey][$relation] = $this->getCachedRelationship($relation, $result, $relationship);
    }