Passed
Push — 5.2 ( 9ea4f6...c9e9f4 )
by liu
03:40
created

Fetch::save()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 8
nop 2
dl 0
loc 21
rs 9.2222
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~2019 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
declare (strict_types = 1);
12
13
namespace think\db;
14
15
use think\App;
16
use think\Exception;
17
18
class Fetch
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
19
{
20
    /**
21
     * 查询对象
22
     * @var Query
23
     */
24
    protected $query;
25
26
    /**
27
     * Connection对象
28
     * @var Connection
29
     */
30
    protected $connection;
31
32
    /**
33
     * Builder对象
34
     * @var Builder
35
     */
36
    protected $builder;
37
38
    /**
39
     * 创建一个查询SQL获取对象
40
     *
41
     * @param  Query    $query      查询对象
1 ignored issue
show
Coding Style introduced by
Expected 1 spaces after parameter name; 6 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 2
Loading history...
42
     */
43
    public function __construct(Query $query)
44
    {
45
        $this->query      = $query;
46
        $this->connection = $query->getConnection();
47
        $this->builder    = $this->connection->getBuilder();
48
    }
49
50
    /**
51
     * 聚合查询
52
     * @access public
53
     * @param  string $aggregate    聚合方法
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
54
     * @param  string $field        字段名
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 8 found
Loading history...
55
     * @return string
56
     */
57
    public function aggregate(string $aggregate, string $field): string
58
    {
59
        $this->query->parseOptions();
60
61
        $field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS tp_' . strtolower($aggregate);
62
63
        return $this->value($field);
64
    }
65
66
    /**
67
     * 得到某个字段的值
68
     * @access public
69
     * @param  string $field   字段名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
70
     * @return string
71
     */
72
    public function value(string $field): string
73
    {
74
        $options = $this->query->parseOptions();
75
76
        if (isset($options['field'])) {
77
            $this->query->removeOption('field');
78
        }
79
80
        $this->query->setOption('field', (array) $field);
81
82
        // 生成查询SQL
83
        $sql = $this->builder->select($this->query, true);
84
85
        if (isset($options['field'])) {
86
            $this->query->setOption('field', $options['field']);
87
        } else {
88
            $this->query->removeOption('field');
89
        }
90
91
        return $this->fetch($sql);
92
    }
93
94
    /**
95
     * 得到某个列的数组
96
     * @access public
97
     * @param  string $field 字段名 多个字段用逗号分隔
98
     * @param  string $key   索引
99
     * @return string
100
     */
101
    public function column(string $field, string $key = ''): string
102
    {
103
        $options = $this->query->parseOptions();
104
105
        if (isset($options['field'])) {
106
            $this->query->removeOption('field');
107
        }
108
109
        if ($key && '*' != $field) {
110
            $field = $key . ',' . $field;
111
        }
112
113
        $field = array_map('trim', explode(',', $field));
114
115
        $this->query->setOption('field', $field);
116
117
        // 生成查询SQL
118
        $sql = $this->builder->select($this->query);
119
120
        if (isset($options['field'])) {
121
            $this->query->setOption('field', $options['field']);
122
        } else {
123
            $this->query->removeOption('field');
124
        }
125
126
        return $this->fetch($sql);
127
    }
128
129
    /**
130
     * 插入记录
131
     * @access public
132
     * @param  array $data 数据
133
     * @return string
134
     */
135
    public function insert(array $data = []): string
136
    {
137
        $options = $this->query->parseOptions();
138
139
        if (!empty($data)) {
140
            $this->query->setOption('data', array_merge($options['data'], $data));
141
        }
142
143
        $replace = $options['replace'] ?? false;
144
        $sql     = $this->builder->insert($this->query, $replace);
145
146
        return $this->fetch($sql);
147
    }
148
149
    /**
150
     * 插入记录并获取自增ID
151
     * @access public
152
     * @param  array $data 数据
153
     * @return string
154
     */
155
    public function insertGetId(array $data = []): string
156
    {
157
        return $this->insert($data);
158
    }
159
160
    /**
161
     * 保存数据 自动判断insert或者update
162
     * @access public
163
     * @param  array $data        数据
164
     * @param  bool  $forceInsert 是否强制insert
165
     * @return string
166
     */
167
    public function save(array $data = [], bool $forceInsert = false): string
168
    {
169
        if (!empty($data)) {
170
            $data = array_merge($this->query->getOptions('data') ?: [], $data);
171
        } else {
172
            $data = $this->query->getOptions('data');
173
        }
174
175
        if (empty($data)) {
176
            return '';
177
        }
178
179
        if ($forceInsert) {
180
            return $this->insert($data);
181
        }
182
183
        $isUpdate = $this->query->parseUpdateData($data);
184
185
        $this->query->setOption('data', $data);
186
187
        return $isUpdate ? $this->update() : $this->insert();
188
    }
189
190
    /**
191
     * 批量插入记录
192
     * @access public
193
     * @param  array     $dataSet 数据集
194
     * @param  boolean   $replace 是否replace
195
     * @param  integer   $limit   每次写入数据限制
196
     * @return string
197
     */
198
    public function insertAll(array $dataSet = [], bool $replace = false, int $limit = null): string
199
    {
200
        $options = $this->query->parseOptions();
201
202
        if (empty($dataSet)) {
203
            $dataSet = $options['data'];
204
        }
205
206
        if (empty($limit) && !empty($options['limit'])) {
207
            $limit = $options['limit'];
208
        }
209
210
        if ($limit) {
211
            $array    = array_chunk($dataSet, $limit, true);
212
            $fetchSql = [];
213
            foreach ($array as $item) {
214
                $sql  = $this->builder->insertAll($this->query, $item, $replace);
215
                $bind = $this->query->getBind();
216
217
                $fetchSql[] = $this->connection->getRealSql($sql, $bind);
218
            }
219
220
            return implode(';', $fetchSql);
221
        }
222
223
        $sql = $this->builder->insertAll($this->query, $dataSet, $replace);
224
225
        return $this->fetch($sql);
226
    }
227
228
    /**
229
     * 通过Select方式插入记录
230
     * @access public
231
     * @param  array    $fields 要插入的数据表字段名
232
     * @param  string   $table  要插入的数据表名
233
     * @return string
234
     */
235
    public function selectInsert(array $fields, string $table): string
236
    {
237
        $this->query->parseOptions();
238
        $sql = $this->builder->selectInsert($this->query, $fields, $table);
239
240
        return $this->fetch($sql);
241
    }
242
243
    /**
244
     * 更新记录
245
     * @access public
246
     * @param  mixed $data 数据
247
     * @return string
248
     */
249
    public function update(array $data = []): string
250
    {
251
        $options = $this->query->parseOptions();
252
253
        $data = array_merge($options['data'], $data);
254
255
        $pk = $this->query->getPk();
256
257
        if (empty($options['where'])) {
258
            // 如果存在主键数据 则自动作为更新条件
259
            if (is_string($pk) && isset($data[$pk])) {
260
                $this->query->where($pk, '=', $data[$pk]);
261
                unset($data[$pk]);
262
            } elseif (is_array($pk)) {
263
                // 增加复合主键支持
264
                foreach ($pk as $field) {
265
                    if (isset($data[$field])) {
266
                        $this->query->where($field, '=', $data[$field]);
267
                    } else {
268
                        // 如果缺少复合主键数据则不执行
269
                        throw new Exception('miss complex primary data');
270
                    }
271
                    unset($data[$field]);
272
                }
273
            }
274
275
            if (empty($this->query->getOptions('where'))) {
276
                // 如果没有任何更新条件则不执行
277
                throw new Exception('miss update condition');
278
            }
279
        }
280
281
        // 更新数据
282
        $this->query->setOption('data', $data);
283
284
        // 生成UPDATE SQL语句
285
        $sql = $this->builder->update($this->query);
286
287
        return $this->fetch($sql);
288
    }
289
290
    /**
291
     * 删除记录
292
     * @access public
293
     * @param  mixed $data 表达式 true 表示强制删除
294
     * @return string
295
     */
296
    public function delete($data = null): string
297
    {
298
        $options = $this->query->parseOptions();
299
300
        if (!is_null($data) && true !== $data) {
301
            // AR模式分析主键条件
302
            $this->query->parsePkWhere($data);
303
        }
304
305
        if (!empty($options['soft_delete'])) {
306
            // 软删除
307
            list($field, $condition) = $options['soft_delete'];
308
            if ($condition) {
309
                $this->query->setOption('soft_delete', null);
310
                $this->query->setOption('data', [$field => $condition]);
311
                // 生成删除SQL语句
312
                $sql = $this->builder->delete($this->query);
313
                return $this->fetch($sql);
314
            }
315
        }
316
317
        // 生成删除SQL语句
318
        $sql = $this->builder->delete($this->query);
319
320
        return $this->fetch($sql);
321
    }
322
323
    /**
324
     * 查找记录 返回SQL
325
     * @access public
326
     * @param  mixed $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
327
     * @return string
328
     */
329
    public function select($data = null): string
330
    {
331
        $this->query->parseOptions();
332
333
        if (!is_null($data)) {
334
            // 主键条件分析
335
            $this->query->parsePkWhere($data);
336
        }
337
338
        // 生成查询SQL
339
        $sql = $this->builder->select($this->query);
340
341
        return $this->fetch($sql);
342
    }
343
344
    /**
345
     * 查找单条记录 返回SQL语句
346
     * @access public
347
     * @param  mixed $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
348
     * @return string
349
     */
350
    public function find($data = null): string
351
    {
352
        $this->query->parseOptions();
353
354
        if (!is_null($data)) {
355
            // AR模式分析主键条件
356
            $this->query->parsePkWhere($data);
357
        }
358
359
        // 生成查询SQL
360
        $sql = $this->builder->select($this->query, true);
361
362
        // 获取实际执行的SQL语句
363
        return $this->fetch($sql);
364
    }
365
366
    /**
367
     * 查找多条记录 如果不存在则抛出异常
368
     * @access public
369
     * @param  mixed $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
370
     * @return string
371
     */
372
    public function selectOrFail($data = null): string
373
    {
374
        return $this->failException(true)->select($data);
0 ignored issues
show
Bug introduced by
The method failException() does not exist on think\db\Fetch. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

374
        return $this->/** @scrutinizer ignore-call */ failException(true)->select($data);
Loading history...
375
    }
376
377
    /**
378
     * 查找单条记录 如果不存在则抛出异常
379
     * @access public
380
     * @param  mixed $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
381
     * @return string
382
     */
383
    public function findOrFail($data = null): string
384
    {
385
        return $this->failException(true)->find($data);
386
    }
387
388
    /**
389
     * 获取实际的SQL语句
390
     * @access public
391
     * @param  string $sql
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
392
     * @return string
393
     */
394
    public function fetch(string $sql): string
395
    {
396
        $bind = $this->query->getBind();
397
398
        return $this->connection->getRealSql($sql, $bind);
399
    }
400
401
    /**
402
     * COUNT查询
403
     * @access public
404
     * @param  string $field 字段名
405
     * @return string
406
     */
407
    public function count(string $field = '*'): string
408
    {
409
        $options = $this->query->parseOptions();
410
411
        if (!empty($options['group'])) {
412
            // 支持GROUP
413
            $bind   = $this->query->getBind();
414
            $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql();
415
416
            $query = $this->query->newQuery()->table([$subSql => '_group_count_']);
417
418
            return $query->fetchsql()->aggregate('COUNT', '*');
419
        } else {
420
            return $this->aggregate('COUNT', $field);
421
        }
422
    }
423
424
    /**
425
     * SUM查询
426
     * @access public
427
     * @param  string $field 字段名
428
     * @return string
429
     */
430
    public function sum(string $field): string
431
    {
432
        return $this->aggregate('SUM', $field);
433
    }
434
435
    /**
436
     * MIN查询
437
     * @access public
438
     * @param  string $field    字段名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
439
     * @return string
440
     */
441
    public function min(string $field): string
442
    {
443
        return $this->aggregate('MIN', $field);
444
    }
445
446
    /**
447
     * MAX查询
448
     * @access public
449
     * @param  string $field    字段名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
450
     * @return string
451
     */
452
    public function max(string $field): string
453
    {
454
        return $this->aggregate('MAX', $field);
455
    }
456
457
    /**
458
     * AVG查询
459
     * @access public
460
     * @param  string $field 字段名
461
     * @return string
462
     */
463
    public function avg(string $field): string
464
    {
465
        return $this->aggregate('AVG', $field);
466
    }
467
468
    public function __call($method, $args)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
469
    {
470
        if (strtolower(substr($method, 0, 5)) == 'getby') {
471
            // 根据某个字段获取记录
472
            $field = App::parseName(substr($method, 5));
473
            return $this->where($field, '=', $args[0])->find();
0 ignored issues
show
Bug introduced by
The method where() does not exist on think\db\Fetch. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

473
            return $this->/** @scrutinizer ignore-call */ where($field, '=', $args[0])->find();
Loading history...
474
        } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
475
            // 根据某个字段获取记录的某个值
476
            $name = App::parseName(substr($method, 10));
477
            return $this->where($name, '=', $args[0])->value($args[1]);
478
        }
479
480
        $result = call_user_func_array([$this->query, $method], $args);
481
        return $result === $this->query ? $this : $result;
482
    }
483
}
484