Passed
Push — 5.1 ( 928868...4bdaa3 )
by liu
09:20
created

HasManyThrough::eagerlyWhere()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 4
nop 5
dl 0
loc 21
rs 9.9332
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
12
namespace think\model\relation;
13
14
use think\db\Query;
15
use think\Exception;
16
use think\Loader;
17
use think\Model;
18
use think\model\Relation;
19
20
class HasManyThrough extends Relation
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
21
{
22
    // 中间关联表外键
23
    protected $throughKey;
24
    // 中间表模型
25
    protected $through;
26
27
    /**
28
     * 中间主键
29
     * @var string
30
     */
31
    protected $throughPk;
32
33
    /**
34
     * 架构函数
35
     * @access public
36
     * @param  Model  $parent     上级模型对象
37
     * @param  string $model      模型名
38
     * @param  string $through    中间模型名
39
     * @param  string $foreignKey 关联外键
40
     * @param  string $throughKey 关联外键
41
     * @param  string $localKey   当前主键
42
     */
43
    public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey)
44
    {
45
        $this->parent     = $parent;
46
        $this->model      = $model;
0 ignored issues
show
Documentation Bug introduced by
It seems like $model of type string is incompatible with the declared type think\Model of property $model.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
47
        $this->through    = (new $through)->db();
48
        $this->foreignKey = $foreignKey;
49
        $this->throughKey = $throughKey;
50
        $this->throughPk  = $this->through->getPk();
51
        $this->localKey   = $localKey;
52
        $this->query      = (new $model)->db();
53
    }
54
55
    /**
56
     * 延迟获取关联数据
57
     * @access public
58
     * @param  string   $subRelation 子关联名
59
     * @param  \Closure $closure     闭包查询条件
60
     * @return \think\Collection
61
     */
62
    public function getRelation($subRelation = '', $closure = null)
63
    {
64
        if ($closure) {
65
            $closure($this->query);
66
        }
67
68
        $this->baseQuery();
69
70
        return $this->query->relation($subRelation)->select();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->rel...$subRelation)->select() also could return the type PDOStatement|array|string which is incompatible with the documented return type think\Collection.
Loading history...
71
    }
72
73
    /**
74
     * 根据关联条件查询当前模型
75
     * @access public
76
     * @param  string  $operator 比较操作符
77
     * @param  integer $count    个数
78
     * @param  string  $id       关联表的统计字段
79
     * @param  string  $joinType JOIN类型
80
     * @return Query
81
     */
82
    public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
83
    {
84
        return $this->parent;
85
    }
86
87
    /**
88
     * 根据关联条件查询当前模型
89
     * @access public
90
     * @param  mixed     $where 查询条件(数组或者闭包)
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
91
     * @param  mixed     $fields 字段
92
     * @return Query
93
     */
94
    public function hasWhere($where = [], $fields = null)
95
    {
96
        throw new Exception('relation not support: hasWhere');
97
    }
98
99
    /**
100
     * 预载入关联查询(数据集)
101
     * @access protected
102
     * @param  array   $resultSet   数据集
103
     * @param  string  $relation    当前关联名
104
     * @param  array   $subRelation 子关联名
105
     * @param  Closure $closure     闭包
106
     * @return void
107
     */
108
    public function eagerlyResultSet(array &$resultSet, string $relation, array $subRelation = [], Closure $closure = null): void
0 ignored issues
show
Bug introduced by
The type think\model\relation\Closure was not found. Did you mean Closure? If so, make sure to prefix the type with \.
Loading history...
109
    {
110
        $localKey   = $this->localKey;
111
        $foreignKey = $this->foreignKey;
112
113
        $range = [];
114
        foreach ($resultSet as $result) {
115
            // 获取关联外键列表
116
            if (isset($result->$localKey)) {
117
                $range[] = $result->$localKey;
118
            }
119
        }
120
121
        if (!empty($range)) {
122
            $this->query->removeWhereField($foreignKey);
123
124
            $data = $this->eagerlyWhere([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
125
                [$this->foreignKey, 'in', $range],
126
            ], $foreignKey, $relation, $subRelation, $closure);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
127
128
            // 关联属性名
129
            $attr = App::parseName($relation);
0 ignored issues
show
Bug introduced by
The type think\model\relation\App was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
130
131
            // 关联数据封装
132
            foreach ($resultSet as $result) {
133
                $pk = $result->$localKey;
134
                if (!isset($data[$pk])) {
135
                    $data[$pk] = [];
136
                }
137
138
                foreach ($data[$pk] as &$relationModel) {
139
                    $relationModel->setParent(clone $result);
140
                }
141
142
                // 设置关联属性
143
                $result->setRelation($attr, $this->resultSetBuild($data[$pk]));
144
            }
145
        }
146
    }
147
148
    /**
149
     * 预载入关联查询(数据)
150
     * @access protected
151
     * @param  Model   $result      数据对象
152
     * @param  string  $relation    当前关联名
153
     * @param  array   $subRelation 子关联名
154
     * @param  Closure $closure     闭包
155
     * @return void
156
     */
157
    public function eagerlyResult(Model $result, string $relation, array $subRelation = [], Closure $closure = null): void
158
    {
159
        $localKey   = $this->localKey;
160
        $foreignKey = $this->foreignKey;
161
        $pk         = $result->$localKey;
162
163
        $this->query->removeWhereField($foreignKey);
164
165
        $data = $this->eagerlyWhere([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
166
            [$foreignKey, '=', $pk],
167
        ], $foreignKey, $relation, $subRelation, $closure);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
168
169
        // 关联数据封装
170
        if (!isset($data[$pk])) {
171
            $data[$pk] = [];
172
        }
173
174
        foreach ($data[$pk] as &$relationModel) {
175
            $relationModel->setParent(clone $result);
176
        }
177
178
        $result->setRelation(App::parseName($relation), $this->resultSetBuild($data[$pk]));
179
    }
180
181
    /**
182
     * 关联模型预查询
183
     * @access public
184
     * @param  array   $where       关联预查询条件
185
     * @param  string  $key         关联键名
186
     * @param  string  $relation    关联名
187
     * @param  array   $subRelation 子关联
188
     * @param  Closure $closure
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
189
     * @return array
190
     */
191
    protected function eagerlyWhere(array $where, string $key, string $relation, array $subRelation = [], Closure $closure = null)
192
    {
193
        // 预载入关联查询 支持嵌套预载入
194
        $throughList = $this->through->where($where)->select();
195
        $keys        = $throughList->column($this->throughPk, $this->throughPk);
196
197
        if ($closure) {
198
            $closure($this->query);
199
        }
200
201
        $list = $this->query->where($this->throughKey, 'in', $keys)->select();
202
203
        // 组装模型数据
204
        $data = [];
205
        $keys = $throughList->column($this->foreignKey, $this->throughPk);
206
207
        foreach ($list as $set) {
208
            $data[$keys[$set->{$this->throughKey}]][] = $set;
209
        }
210
211
        return $data;
212
    }
213
214
    /**
215
     * 关联统计
216
     * @access public
217
     * @param  Model   $result  数据对象
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 2 found
Loading history...
218
     * @param  Closure $closure 闭包
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
219
     * @param  string  $aggregate 聚合查询方法
220
     * @param  string  $field 字段
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
221
     * @param  string  $name 统计字段别名
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter name; 1 found
Loading history...
222
     * @return integer
223
     */
224
    public function relationCount(Model $result, Closure $closure, string $aggregate = 'count', string $field = '*', string &$name = null)
225
    {
226
        $localKey = $this->localKey;
227
228
        if (!isset($result->$localKey)) {
229
            return 0;
230
        }
231
232
        if ($closure) {
0 ignored issues
show
introduced by
$closure is of type think\model\relation\Closure, thus it always evaluated to true.
Loading history...
233
            $return = $closure($this->query);
234
            if ($return && is_string($return)) {
235
                $name = $return;
236
            }
237
        }
238
239
        $alias        = App::parseName(App::classBaseName($this->model));
240
        $throughTable = $this->through->getTable();
241
        $pk           = $this->throughPk;
242
        $throughKey   = $this->throughKey;
243
        $modelTable   = $this->parent->getTable();
244
245
        if (false === strpos($field, '.')) {
246
            $field = $alias . '.' . $field;
247
        }
248
249
        return $this->query
250
            ->alias($alias)
251
            ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
252
            ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
253
            ->where($throughTable . '.' . $this->foreignKey, $result->$localKey)
254
            ->$aggregate($field);
255
    }
256
257
    /**
258
     * 创建关联统计子查询
259
     * @access public
260
     * @param  Closure $closure 闭包
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
261
     * @param  string  $aggregate 聚合查询方法
262
     * @param  string  $field 字段
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
263
     * @param  string  $name 统计字段别名
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter name; 1 found
Loading history...
264
     * @return string
265
     */
266
    public function getRelationCountQuery(Closure $closure = null, string $aggregate = 'count', string $field = '*', string &$name = null): string
267
    {
268
        if ($closure) {
269
            $return = $closure($this->query);
270
            if ($return && is_string($return)) {
271
                $name = $return;
272
            }
273
        }
274
275
        $alias        = App::parseName(App::classBaseName($this->model));
276
        $throughTable = $this->through->getTable();
277
        $pk           = $this->throughPk;
278
        $throughKey   = $this->throughKey;
279
        $modelTable   = $this->parent->getTable();
280
281
        if (false === strpos($field, '.')) {
282
            $field = $alias . '.' . $field;
283
        }
284
285
        return $this->query
286
            ->alias($alias)
287
            ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
288
            ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
289
            ->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
290
            ->fetchSql()
291
            ->$aggregate($field);
292
    }
293
294
    /**
295
     * 执行基础查询(仅执行一次)
296
     * @access protected
297
     * @return void
298
     */
299
    protected function baseQuery()
300
    {
301
        if (empty($this->baseQuery) && $this->parent->getData()) {
302
            $alias        = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
303
            $throughTable = $this->through->getTable();
304
            $pk           = $this->throughPk;
305
            $throughKey   = $this->throughKey;
306
            $modelTable   = $this->parent->getTable();
307
            $fields       = $this->getQueryFields($alias);
308
309
            $this->query
310
                ->field($fields)
311
                ->alias($alias)
312
                ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
313
                ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
314
                ->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
315
316
            $this->baseQuery = true;
317
        }
318
    }
319
320
}
321