Completed
Push — 6.0 ( 52d3ce...17b267 )
by liu
02:46
created

Query::save()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 5
nop 2
dl 0
loc 15
ccs 0
cts 8
cp 0
crap 20
rs 10
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 Closure;
16
use PDO;
17
use PDOStatement;
18
use think\App;
19
use think\Collection;
20
use think\Db;
21
use think\db\exception\BindParamException;
22
use think\db\exception\DataNotFoundException;
23
use think\db\exception\ModelNotFoundException;
24
use think\Exception;
25
use think\exception\DbException;
26
use think\exception\PDOException;
27
use think\Model;
28
use think\model\Collection as ModelCollection;
29
use think\model\Relation;
30
use think\model\relation\OneToOne;
31
use think\Paginator;
32
33
class Query
1 ignored issue
show
Coding Style introduced by
Missing doc comment for class Query
Loading history...
34
{
35
    /**
36
     * 当前数据库连接对象
37
     * @var Connection
38
     */
39
    protected $connection;
40
41
    /**
42
     * 当前模型对象
43
     * @var Model
44
     */
45
    protected $model;
46
47
    /**
48
     * Db对象
49
     * @var Db
50
     */
51
    protected $db;
52
53
    /**
54
     * 当前数据表名称(不含前缀)
55
     * @var string
56
     */
57
    protected $name = '';
58
59
    /**
60
     * 当前数据表主键
61
     * @var string|array
62
     */
63
    protected $pk;
64
65
    /**
66
     * 当前数据表前缀
67
     * @var string
68
     */
69
    protected $prefix = '';
70
71
    /**
72
     * 当前查询参数
73
     * @var array
74
     */
75
    protected $options = [];
76
77
    /**
78
     * 当前参数绑定
79
     * @var array
80
     */
81
    protected $bind = [];
82
83
    /**
84
     * 日期查询表达式
85
     * @var array
86
     */
87
    protected $timeRule = [
88
        'today'      => ['today', 'tomorrow'],
89
        'yesterday'  => ['yesterday', 'today'],
90
        'week'       => ['this week 00:00:00', 'next week 00:00:00'],
91
        'last week'  => ['last week 00:00:00', 'this week 00:00:00'],
92
        'month'      => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'],
93
        'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'],
94
        'year'       => ['this year 1/1', 'next year 1/1'],
95
        'last year'  => ['last year 1/1', 'this year 1/1'],
96
    ];
97
98
    /**
99
     * 架构函数
100
     * @access public
101
     * @param Connection $connection 数据库连接对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
102
     */
103
    public function __construct(Connection $connection)
104
    {
105
        $this->connection = $connection;
106
107
        $this->prefix = $this->connection->getConfig('prefix');
108
    }
109
110
    /**
111
     * 创建一个新的查询对象
112
     * @access public
113
     * @return Query
114
     */
115
    public function newQuery()
116
    {
117
        $query = new static($this->connection);
118
119
        if ($this->model) {
120
            $query->model($this->model);
121
        }
122
123
        if (isset($this->options['table'])) {
124
            $query->table($this->options['table']);
125
        } else {
126
            $query->name($this->name);
127
        }
128
129
        $query->setDb($this->db);
130
131
        return $query;
132
    }
133
134
    /**
135
     * 利用__call方法实现一些特殊的Model方法
136
     * @access public
137
     * @param string $method 方法名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
138
     * @param array  $args   调用参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
139
     * @return mixed
140
     * @throws DbException
141
     * @throws Exception
142
     */
143
    public function __call(string $method, array $args)
144
    {
145
        if (strtolower(substr($method, 0, 5)) == 'getby') {
146
            // 根据某个字段获取记录
147
            $field = App::parseName(substr($method, 5));
148
            return $this->where($field, '=', $args[0])->find();
149
        } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
150
            // 根据某个字段获取记录的某个值
151
            $name = App::parseName(substr($method, 10));
152
            return $this->where($name, '=', $args[0])->value($args[1]);
153
        } elseif (strtolower(substr($method, 0, 7)) == 'whereor') {
154
            $name = App::parseName(substr($method, 7));
155
            array_unshift($args, $name);
156
            return call_user_func_array([$this, 'whereOr'], $args);
157
        } elseif (strtolower(substr($method, 0, 5)) == 'where') {
158
            $name = App::parseName(substr($method, 5));
159
            array_unshift($args, $name);
160
            return call_user_func_array([$this, 'where'], $args);
161
        } elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
162
            // 动态调用命名范围
163
            $method = 'scope' . $method;
164
            array_unshift($args, $this);
165
166
            call_user_func_array([$this->model, $method], $args);
167
            return $this;
168
        } else {
169
            throw new Exception('method not exist:' . static::class . '->' . $method);
170
        }
171
    }
172
173
    /**
174
     * 获取当前的数据库Connection对象
175
     * @access public
176
     * @return Connection
177
     */
178
    public function getConnection()
179
    {
180
        return $this->connection;
181
    }
182
183
    /**
184
     * 设置当前的数据库Connection对象
185
     * @access public
186
     * @param Connection $connection 数据库连接对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
187
     * @return $this
188
     */
189
    public function setConnection($connection)
190
    {
191
        $this->connection = $connection;
192
193
        return $this;
194
    }
195
196
    /**
197
     * 设置Db对象
198
     * @access public
199
     * @param Db $db
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
200
     * @return $this
201
     */
202
    public function setDb(Db $db)
203
    {
204
        $this->db = $db;
205
        $this->connection->setDb($db);
206
        return $this;
207
    }
208
209
    /**
210
     * 获取Db对象
211
     * @access public
212
     * @return Db
213
     */
214
    public function getDb()
215
    {
216
        return $this->db;
217
    }
218
219
    /**
220
     * 指定模型
221
     * @access public
222
     * @param Model $model 模型对象实例
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
223
     * @return $this
224
     */
225
    public function model(Model $model)
226
    {
227
        $this->model = $model;
228
        return $this;
229
    }
230
231
    /**
232
     * 获取当前的模型对象
233
     * @access public
234
     * @return Model|null
235
     */
236
    public function getModel()
237
    {
238
        return $this->model ? $this->model->setQuery($this) : null;
239
    }
240
241
    /**
242
     * 指定当前数据表名(不含前缀)
243
     * @access public
244
     * @param string $name 不含前缀的数据表名字
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
245
     * @return $this
246
     */
247
    public function name(string $name)
248
    {
249
        $this->name = $name;
250
        return $this;
251
    }
252
253
    /**
254
     * 获取当前的数据表名称
255
     * @access public
256
     * @return string
257
     */
258
    public function getName(): string
259
    {
260
        return $this->name ?: $this->model->getName();
261
    }
262
263
    /**
264
     * 获取数据库的配置参数
265
     * @access public
266
     * @param string $name 参数名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
267
     * @return mixed
268
     */
269
    public function getConfig(string $name = '')
270
    {
271
        return $this->connection->getConfig($name);
272
    }
273
274
    /**
275
     * 得到当前或者指定名称的数据表
276
     * @access public
277
     * @param string $name 不含前缀的数据表名字
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
278
     * @return mixed
279
     */
280
    public function getTable(string $name = '')
281
    {
282
        if (empty($name) && isset($this->options['table'])) {
283
            return $this->options['table'];
284
        }
285
286
        $name = $name ?: $this->name;
287
288
        return $this->prefix . App::parseName($name);
289
    }
290
291
    /**
292
     * 获取数据表字段信息
293
     * @access public
294
     * @param string $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
295
     * @return array
296
     */
297
    public function getTableFields($tableName = ''): array
298
    {
299
        if ('' == $tableName) {
300
            $tableName = $this->getTable();
301
        }
302
303
        return $this->connection->getTableFields($tableName);
304
    }
305
306
    /**
307
     * 设置字段类型信息
308
     * @access public
309
     * @param array $type 字段类型信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
310
     * @return $this
311
     */
312
    public function setFieldType(array $type)
313
    {
314
        $this->options['field_type'] = $type;
315
        return $this;
316
    }
317
318
    /**
319
     * 获取字段类型信息
320
     * @access public
321
     * @return array
322
     */
323
    public function getFieldsType(): array
324
    {
325
        if (!empty($this->options['field_type'])) {
326
            return $this->options['field_type'];
327
        }
328
329
        return $this->connection->getFieldsType($this->getTable());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connection...Type($this->getTable()) could return the type string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
330
    }
331
332
    /**
333
     * 获取字段类型信息
334
     * @access public
335
     * @param string $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
336
     * @return string|null
337
     */
338
    public function getFieldType(string $field)
339
    {
340
        $fieldType = $this->getFieldsType();
341
342
        return $fieldType[$field] ?? null;
343
    }
344
345
    /**
346
     * 获取字段类型信息
347
     * @access public
348
     * @return array
349
     */
350
    public function getFieldsBindType(): array
351
    {
352
        $fieldType = $this->getFieldsType();
353
354
        return array_map([$this->connection, 'getFieldBindType'], $fieldType);
355
    }
356
357
    /**
358
     * 获取字段类型信息
359
     * @access public
360
     * @param string $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
361
     * @return int
362
     */
363
    public function getFieldBindType(string $field): int
364
    {
365
        $fieldType = $this->getFieldType($field);
366
367
        return $this->connection->getFieldBindType($fieldType ?: '');
368
    }
369
370
    /**
371
     * 执行查询 返回数据集
372
     * @access public
373
     * @param string $sql  sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
374
     * @param array  $bind 参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
375
     * @return array
376
     * @throws BindParamException
377
     * @throws PDOException
378
     */
379
    public function query(string $sql, array $bind = []): array
380
    {
381
        return $this->connection->query($this, $sql, $bind, true);
382
    }
383
384
    /**
385
     * 执行语句
386
     * @access public
387
     * @param string $sql  sql指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
388
     * @param array  $bind 参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
389
     * @return int
390
     * @throws BindParamException
391
     * @throws PDOException
392
     */
393
    public function execute(string $sql, array $bind = []): int
394
    {
395
        return $this->connection->execute($this, $sql, $bind, true);
396
    }
397
398
    /**
399
     * 监听SQL执行
400
     * @access public
401
     * @param callable $callback 回调方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
402
     * @return void
403
     */
404
    public function listen(callable $callback): void
405
    {
406
        $this->connection->listen($callback);
407
    }
408
409
    /**
410
     * 获取最近插入的ID
411
     * @access public
412
     * @param string $sequence 自增序列名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
413
     * @return string
414
     */
415
    public function getLastInsID(string $sequence = null): string
416
    {
417
        return $this->connection->getLastInsID($sequence);
418
    }
419
420
    /**
421
     * 获取返回或者影响的记录数
422
     * @access public
423
     * @return integer
424
     */
425
    public function getNumRows(): int
426
    {
427
        return $this->connection->getNumRows();
428
    }
429
430
    /**
431
     * 获取最近一次查询的sql语句
432
     * @access public
433
     * @return string
434
     */
435
    public function getLastSql(): string
436
    {
437
        return $this->connection->getLastSql();
438
    }
439
440
    /**
441
     * 执行数据库事务
442
     * @access public
443
     * @param callable $callback 数据操作方法回调
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
444
     * @return mixed
445
     */
446
    public function transaction(callable $callback)
447
    {
448
        return $this->connection->transaction($callback);
449
    }
450
451
    /**
452
     * 启动事务
453
     * @access public
454
     * @return void
455
     */
456
    public function startTrans(): void
457
    {
458
        $this->connection->startTrans();
459
    }
460
461
    /**
462
     * 用于非自动提交状态下面的查询提交
463
     * @access public
464
     * @return void
465
     * @throws PDOException
466
     */
467
    public function commit(): void
468
    {
469
        $this->connection->commit();
470
    }
471
472
    /**
473
     * 事务回滚
474
     * @access public
475
     * @return void
476
     * @throws PDOException
477
     */
478
    public function rollback(): void
479
    {
480
        $this->connection->rollback();
481
    }
482
483
    /**
484
     * 批处理执行SQL语句
485
     * 批处理的指令都认为是execute操作
486
     * @access public
487
     * @param array $sql SQL批处理指令
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
488
     * @return bool
489
     */
490
    public function batchQuery(array $sql = []): bool
491
    {
492
        return $this->connection->batchQuery($this, $sql);
493
    }
494
495
    /**
496
     * 得到某个字段的值
497
     * @access public
498
     * @param string $field   字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
499
     * @param mixed  $default 默认值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
500
     * @return mixed
501
     */
502
    public function value(string $field, $default = null)
503
    {
504
        return $this->connection->value($this, $field, $default);
505
    }
506
507
    /**
508
     * 得到某个列的数组
509
     * @access public
510
     * @param string $field 字段名 多个字段用逗号分隔
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
511
     * @param string $key   索引
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
512
     * @return array
513
     */
514
    public function column(string $field, string $key = ''): array
515
    {
516
        return $this->connection->column($this, $field, $key);
517
    }
518
519
    /**
520
     * 聚合查询
521
     * @access protected
522
     * @param string     $aggregate 聚合方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
523
     * @param string|Raw $field     字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
524
     * @param bool       $force     强制转为数字类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
525
     * @return mixed
526
     */
527
    protected function aggregate(string $aggregate, $field, bool $force = false)
528
    {
529
        return $this->connection->aggregate($this, $aggregate, $field, $force);
530
    }
531
532
    /**
533
     * COUNT查询
534
     * @access public
535
     * @param string|Raw $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
536
     * @return int
537
     */
538
    public function count(string $field = '*'): int
539
    {
540
        if (!empty($this->options['group'])) {
541
            // 支持GROUP
542
            $options = $this->getOptions();
543
            $subSql  = $this->options($options)->field('count(' . $field . ') AS think_count')->bind($this->bind)->buildSql();
544
545
            $query = $this->newQuery()->table([$subSql => '_group_count_']);
546
547
            $count = $query->aggregate('COUNT', '*');
548
        } else {
549
            $count = $this->aggregate('COUNT', $field);
550
        }
551
552
        return (int) $count;
553
    }
554
555
    /**
556
     * SUM查询
557
     * @access public
558
     * @param string|Raw $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
559
     * @return float
560
     */
561
    public function sum($field): float
562
    {
563
        return $this->aggregate('SUM', $field, true);
564
    }
565
566
    /**
567
     * MIN查询
568
     * @access public
569
     * @param string|Raw $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
570
     * @param bool       $force 强制转为数字类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
571
     * @return mixed
572
     */
573
    public function min($field, bool $force = true)
574
    {
575
        return $this->aggregate('MIN', $field, $force);
576
    }
577
578
    /**
579
     * MAX查询
580
     * @access public
581
     * @param string|Raw $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
582
     * @param bool       $force 强制转为数字类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
583
     * @return mixed
584
     */
585
    public function max($field, bool $force = true)
586
    {
587
        return $this->aggregate('MAX', $field, $force);
588
    }
589
590
    /**
591
     * AVG查询
592
     * @access public
593
     * @param string|Raw $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
594
     * @return float
595
     */
596
    public function avg($field): float
597
    {
598
        return $this->aggregate('AVG', $field, true);
599
    }
600
601
    /**
602
     * 查询SQL组装 join
603
     * @access public
604
     * @param mixed  $join      关联的表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
605
     * @param mixed  $condition 条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
606
     * @param string $type      JOIN类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
607
     * @param array  $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
608
     * @return $this
609
     */
610
    public function join($join, string $condition = null, string $type = 'INNER', array $bind = [])
611
    {
612
        $table = $this->getJoinTable($join);
613
614
        if (!empty($bind) && $condition) {
615
            $this->bindParams($condition, $bind);
616
        }
617
618
        $this->options['join'][] = [$table, strtoupper($type), $condition];
619
620
        return $this;
621
    }
622
623
    /**
624
     * LEFT JOIN
625
     * @access public
626
     * @param mixed $join      关联的表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
627
     * @param mixed $condition 条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
628
     * @param array $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
629
     * @return $this
630
     */
631
    public function leftJoin($join, string $condition = null, array $bind = [])
632
    {
633
        return $this->join($join, $condition, 'LEFT', $bind);
634
    }
635
636
    /**
637
     * RIGHT JOIN
638
     * @access public
639
     * @param mixed $join      关联的表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
640
     * @param mixed $condition 条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
641
     * @param array $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
642
     * @return $this
643
     */
644
    public function rightJoin($join, string $condition = null, array $bind = [])
645
    {
646
        return $this->join($join, $condition, 'RIGHT', $bind);
647
    }
648
649
    /**
650
     * FULL JOIN
651
     * @access public
652
     * @param mixed $join      关联的表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
653
     * @param mixed $condition 条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
654
     * @param array $bind      参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
655
     * @return $this
656
     */
657
    public function fullJoin($join, string $condition = null, array $bind = [])
658
    {
659
        return $this->join($join, $condition, 'FULL');
660
    }
661
662
    /**
663
     * 获取Join表名及别名 支持
664
     * ['prefix_table或者子查询'=>'alias'] 'table alias'
665
     * @access protected
666
     * @param array|string|Raw $join  JION表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
667
     * @param string           $alias 别名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
668
     * @return string|array
669
     */
670
    protected function getJoinTable($join, &$alias = null)
671
    {
672
        if (is_array($join)) {
673
            $table = $join;
674
            $alias = array_shift($join);
675
            return $table;
676
        } elseif ($join instanceof Raw) {
677
            return $join;
678
        }
679
680
        $join = trim($join);
681
682
        if (false !== strpos($join, '(')) {
683
            // 使用子查询
684
            $table = $join;
685
        } else {
686
            // 使用别名
687
            if (strpos($join, ' ')) {
688
                // 使用别名
689
                list($table, $alias) = explode(' ', $join);
690
            } else {
691
                $table = $join;
692
                if (false === strpos($join, '.')) {
693
                    $alias = $join;
694
                }
695
            }
696
697
            if ($this->prefix && false === strpos($table, '.') && 0 !== strpos($table, $this->prefix)) {
698
                $table = $this->getTable($table);
699
            }
700
        }
701
702
        if (!empty($alias) && $table != $alias) {
703
            $table = [$table => $alias];
704
        }
705
706
        return $table;
707
    }
708
709
    /**
710
     * 查询SQL组装 union
711
     * @access public
712
     * @param mixed   $union UNION
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
713
     * @param boolean $all   是否适用UNION ALL
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
714
     * @return $this
715
     */
716
    public function union($union, bool $all = false)
717
    {
718
        $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
719
720
        if (is_array($union)) {
721
            $this->options['union'] = array_merge($this->options['union'], $union);
722
        } else {
723
            $this->options['union'][] = $union;
724
        }
725
726
        return $this;
727
    }
728
729
    /**
730
     * 查询SQL组装 union all
731
     * @access public
732
     * @param mixed $union UNION数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
733
     * @return $this
734
     */
735
    public function unionAll($union)
736
    {
737
        return $this->union($union, true);
738
    }
739
740
    /**
741
     * 指定查询字段 支持字段排除和指定数据表
742
     * @access public
743
     * @param mixed   $field     字段信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
744
     * @param boolean $except    是否排除
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
745
     * @param string  $tableName 数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
746
     * @param string  $prefix    字段前缀
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
747
     * @param string  $alias     别名前缀
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
748
     * @return $this
749
     */
750
    public function field($field, bool $except = false, string $tableName = '', string $prefix = '', string $alias = '')
751
    {
752
        if (empty($field)) {
753
            return $this;
754
        } elseif ($field instanceof Raw) {
755
            $this->options['field'][] = $field;
756
            return $this;
757
        }
758
759
        if (is_string($field)) {
760
            if (preg_match('/[\<\'\"\(]/', $field)) {
761
                return $this->fieldRaw($field);
762
            }
763
764
            $field = array_map('trim', explode(',', $field));
765
        }
766
767
        if (true === $field) {
768
            // 获取全部字段
769
            $fields = $this->getTableFields($tableName);
770
            $field  = $fields ?: ['*'];
0 ignored issues
show
introduced by
$fields is an empty array, thus is always false.
Loading history...
771
        } elseif ($except) {
772
            // 字段排除
773
            $fields = $this->getTableFields($tableName);
774
            $field  = $fields ? array_diff($fields, $field) : $field;
0 ignored issues
show
introduced by
$fields is an empty array, thus is always false.
Loading history...
775
        }
776
777
        if ($tableName) {
778
            // 添加统一的前缀
779
            $prefix = $prefix ?: $tableName;
780
            foreach ($field as $key => &$val) {
781
                if (is_numeric($key) && $alias) {
782
                    $field[$prefix . '.' . $val] = $alias . $val;
783
                    unset($field[$key]);
784
                } elseif (is_numeric($key)) {
785
                    $val = $prefix . '.' . $val;
786
                }
787
            }
788
        }
789
790
        if (isset($this->options['field'])) {
791
            $field = array_merge((array) $this->options['field'], $field);
792
        }
793
794
        $this->options['field'] = array_unique($field);
795
796
        return $this;
797
    }
798
799
    /**
800
     * 表达式方式指定查询字段
801
     * @access public
802
     * @param string $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
803
     * @return $this
804
     */
805
    public function fieldRaw(string $field)
806
    {
807
        $this->options['field'][] = new Raw($field);
808
809
        return $this;
810
    }
811
812
    /**
813
     * 设置数据
814
     * @access public
815
     * @param array $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
816
     * @return $this
817
     */
818
    public function data(array $data)
819
    {
820
        $this->options['data'] = $data;
821
822
        return $this;
823
    }
824
825
    /**
826
     * 字段值增长
827
     * @access public
828
     * @param string  $field    字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
829
     * @param integer $step     增长值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
830
     * @param integer $lazyTime 延时时间(s)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
831
     * @param string  $op       INC/DEC
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
832
     * @return $this
833
     */
834
    public function inc(string $field, int $step = 1, int $lazyTime = 0, string $op = 'INC')
835
    {
836
        if ($lazyTime > 0) {
837
            // 延迟写入
838
            $condition = $this->options['where'] ?? [];
839
840
            $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
841
            $step = $this->connection->lazyWrite($op, $guid, $step, $lazyTime);
842
843
            if (false === $step) {
844
                return $this;
845
            }
846
847
            $op = 'INC';
848
        }
849
850
        $this->options['data'][$field] = [$op, $step];
851
852
        return $this;
853
    }
854
855
    /**
856
     * 字段值减少
857
     * @access public
858
     * @param string  $field    字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
859
     * @param integer $step     增长值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
860
     * @param integer $lazyTime 延时时间(s)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
861
     * @return $this
862
     */
863
    public function dec(string $field, int $step = 1, int $lazyTime = 0)
864
    {
865
        return $this->inc($field, $step, $lazyTime, 'DEC');
866
    }
867
868
    /**
869
     * 使用表达式设置数据
870
     * @access public
871
     * @param string $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
872
     * @param string $value 字段值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
873
     * @return $this
874
     */
875
    public function exp(string $field, string $value)
876
    {
877
        $this->options['data'][$field] = new Raw($value);
878
        return $this;
879
    }
880
881
    /**
882
     * 指定JOIN查询字段
883
     * @access public
884
     * @param string|array $join  数据表
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
885
     * @param string|array $field 查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
886
     * @param string       $on    JOIN条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
887
     * @param string       $type  JOIN类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
888
     * @param array        $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
889
     * @return $this
890
     */
891
    public function view($join, $field = true, $on = null, string $type = 'INNER', array $bind = [])
892
    {
893
        $this->options['view'] = true;
894
895
        $fields = [];
896
        $table  = $this->getJoinTable($join, $alias);
897
898
        if (true === $field) {
899
            $fields = $alias . '.*';
900
        } else {
901
            if (is_string($field)) {
902
                $field = explode(',', $field);
903
            }
904
905
            foreach ($field as $key => $val) {
906
                if (is_numeric($key)) {
907
                    $fields[] = $alias . '.' . $val;
908
909
                    $this->options['map'][$val] = $alias . '.' . $val;
910
                } else {
911
                    if (preg_match('/[,=\.\'\"\(\s]/', $key)) {
912
                        $name = $key;
913
                    } else {
914
                        $name = $alias . '.' . $key;
915
                    }
916
917
                    $fields[] = $name . ' AS ' . $val;
918
919
                    $this->options['map'][$val] = $name;
920
                }
921
            }
922
        }
923
924
        $this->field($fields);
925
926
        if ($on) {
927
            $this->join($table, $on, $type, $bind);
928
        } else {
929
            $this->table($table);
930
        }
931
932
        return $this;
933
    }
934
935
    /**
936
     * 指定AND查询条件
937
     * @access public
938
     * @param mixed $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
939
     * @param mixed $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
940
     * @param mixed $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
941
     * @return $this
942
     */
943
    public function where($field, $op = null, $condition = null)
944
    {
945
        if ($field instanceof $this) {
946
            $this->options['where'] = $field->getOptions('where');
947
            return $this;
948
        }
949
950
        $param = func_get_args();
951
        array_shift($param);
952
        return $this->parseWhereExp('AND', $field, $op, $condition, $param);
953
    }
954
955
    /**
956
     * 指定OR查询条件
957
     * @access public
958
     * @param mixed $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
959
     * @param mixed $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
960
     * @param mixed $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
961
     * @return $this
962
     */
963
    public function whereOr($field, $op = null, $condition = null)
964
    {
965
        $param = func_get_args();
966
        array_shift($param);
967
        return $this->parseWhereExp('OR', $field, $op, $condition, $param);
968
    }
969
970
    /**
971
     * 指定XOR查询条件
972
     * @access public
973
     * @param mixed $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
974
     * @param mixed $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
975
     * @param mixed $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
976
     * @return $this
977
     */
978
    public function whereXor($field, $op = null, $condition = null)
979
    {
980
        $param = func_get_args();
981
        array_shift($param);
982
        return $this->parseWhereExp('XOR', $field, $op, $condition, $param);
983
    }
984
985
    /**
986
     * 指定Null查询条件
987
     * @access public
988
     * @param mixed  $field 查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
989
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
990
     * @return $this
991
     */
992
    public function whereNull(string $field, string $logic = 'AND')
993
    {
994
        return $this->parseWhereExp($logic, $field, 'NULL', null, [], true);
995
    }
996
997
    /**
998
     * 指定NotNull查询条件
999
     * @access public
1000
     * @param mixed  $field 查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1001
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1002
     * @return $this
1003
     */
1004
    public function whereNotNull(string $field, string $logic = 'AND')
1005
    {
1006
        return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true);
1007
    }
1008
1009
    /**
1010
     * 指定Exists查询条件
1011
     * @access public
1012
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1013
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1014
     * @return $this
1015
     */
1016
    public function whereExists($condition, string $logic = 'AND')
1017
    {
1018
        if (is_string($condition)) {
1019
            $condition = new Raw($condition);
1020
        }
1021
1022
        $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition];
1023
        return $this;
1024
    }
1025
1026
    /**
1027
     * 指定NotExists查询条件
1028
     * @access public
1029
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1030
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1031
     * @return $this
1032
     */
1033
    public function whereNotExists($condition, string $logic = 'AND')
1034
    {
1035
        if (is_string($condition)) {
1036
            $condition = new Raw($condition);
1037
        }
1038
1039
        $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition];
1040
        return $this;
1041
    }
1042
1043
    /**
1044
     * 指定In查询条件
1045
     * @access public
1046
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1047
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1048
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1049
     * @return $this
1050
     */
1051
    public function whereIn(string $field, $condition, string $logic = 'AND')
1052
    {
1053
        return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true);
1054
    }
1055
1056
    /**
1057
     * 指定NotIn查询条件
1058
     * @access public
1059
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1060
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1061
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1062
     * @return $this
1063
     */
1064
    public function whereNotIn(string $field, $condition, string $logic = 'AND')
1065
    {
1066
        return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true);
1067
    }
1068
1069
    /**
1070
     * 指定Like查询条件
1071
     * @access public
1072
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1073
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1074
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1075
     * @return $this
1076
     */
1077
    public function whereLike(string $field, $condition, string $logic = 'AND')
1078
    {
1079
        return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true);
1080
    }
1081
1082
    /**
1083
     * 指定NotLike查询条件
1084
     * @access public
1085
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1086
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1087
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1088
     * @return $this
1089
     */
1090
    public function whereNotLike(string $field, $condition, string $logic = 'AND')
1091
    {
1092
        return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true);
1093
    }
1094
1095
    /**
1096
     * 指定Between查询条件
1097
     * @access public
1098
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1099
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1100
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1101
     * @return $this
1102
     */
1103
    public function whereBetween(string $field, $condition, string $logic = 'AND')
1104
    {
1105
        return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true);
1106
    }
1107
1108
    /**
1109
     * 指定NotBetween查询条件
1110
     * @access public
1111
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1112
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1113
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1114
     * @return $this
1115
     */
1116
    public function whereNotBetween(string $field, $condition, string $logic = 'AND')
1117
    {
1118
        return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true);
1119
    }
1120
1121
    /**
1122
     * 指定FIND_IN_SET查询条件
1123
     * @access public
1124
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1125
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1126
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1127
     * @return $this
1128
     */
1129
    public function whereFindInSet(string $field, $condition, string $logic = 'AND')
1130
    {
1131
        return $this->parseWhereExp($logic, $field, 'FIND IN SET', $condition, [], true);
1132
    }
1133
1134
    /**
1135
     * 比较两个字段
1136
     * @access public
1137
     * @param string $field1   查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1138
     * @param string $operator 比较操作符
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1139
     * @param string $field2   比较字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1140
     * @param string $logic    查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1141
     * @return $this
1142
     */
1143
    public function whereColumn(string $field1, string $operator, string $field2 = null, string $logic = 'AND')
1144
    {
1145
        if (is_null($field2)) {
1146
            $field2   = $operator;
1147
            $operator = '=';
1148
        }
1149
1150
        return $this->parseWhereExp($logic, $field1, 'COLUMN', [$operator, $field2], [], true);
1151
    }
1152
1153
    /**
1154
     * 设置软删除字段及条件
1155
     * @access public
1156
     * @param string $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1157
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1158
     * @return $this
1159
     */
1160
    public function useSoftDelete(string $field, $condition = null)
1161
    {
1162
        if ($field) {
1163
            $this->options['soft_delete'] = [$field, $condition];
1164
        }
1165
1166
        return $this;
1167
    }
1168
1169
    /**
1170
     * 指定Exp查询条件
1171
     * @access public
1172
     * @param mixed  $field 查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1173
     * @param string $where 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1174
     * @param array  $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1175
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1176
     * @return $this
1177
     */
1178
    public function whereExp(string $field, string $where, array $bind = [], string $logic = 'AND')
1179
    {
1180
        if (!empty($bind)) {
1181
            $this->bindParams($where, $bind);
1182
        }
1183
1184
        $this->options['where'][$logic][] = [$field, 'EXP', new Raw($where)];
1185
1186
        return $this;
1187
    }
1188
1189
    /**
1190
     * 指定字段Raw查询
1191
     * @access public
1192
     * @param string $field     查询字段表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1193
     * @param mixed  $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1194
     * @param string $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1195
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1196
     * @return $this
1197
     */
1198
    public function whereFieldRaw(string $field, $op, $condition = null, string $logic = 'AND')
1199
    {
1200
        if (is_null($condition)) {
1201
            $condition = $op;
1202
            $op        = '=';
1203
        }
1204
1205
        $this->options['where'][$logic][] = [new Raw($field), $op, $condition];
1206
        return $this;
1207
    }
1208
1209
    /**
1210
     * 指定表达式查询条件
1211
     * @access public
1212
     * @param string $where 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1213
     * @param array  $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1214
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1215
     * @return $this
1216
     */
1217
    public function whereRaw(string $where, array $bind = [], string $logic = 'AND')
1218
    {
1219
        if (!empty($bind)) {
1220
            $this->bindParams($where, $bind);
1221
        }
1222
1223
        $this->options['where'][$logic][] = new Raw($where);
1224
1225
        return $this;
1226
    }
1227
1228
    /**
1229
     * 指定表达式查询条件 OR
1230
     * @access public
1231
     * @param string $where 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1232
     * @param array  $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1233
     * @return $this
1234
     */
1235
    public function whereOrRaw(string $where, array $bind = [])
1236
    {
1237
        return $this->whereRaw($where, $bind, 'OR');
1238
    }
1239
1240
    /**
1241
     * 分析查询表达式
1242
     * @access protected
1243
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1244
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1245
     * @param mixed  $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1246
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1247
     * @param array  $param     查询参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1248
     * @param bool   $strict    严格模式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1249
     * @return $this
1250
     */
1251
    protected function parseWhereExp(string $logic, $field, $op, $condition, array $param = [], bool $strict = false)
1252
    {
1253
        $logic = strtoupper($logic);
1254
1255
        if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) {
1256
            $field = $this->options['via'] . '.' . $field;
1257
        }
1258
1259
        if ($field instanceof Raw) {
1260
            return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
1261
        } elseif ($strict) {
1262
            // 使用严格模式查询
1263
            if ('=' == $op) {
1264
                $where = $this->whereEq($field, $condition);
1265
            } else {
1266
                $where = [$field, $op, $condition, $logic];
1267
            }
1268
        } elseif (is_array($field)) {
1269
            // 解析数组批量查询
1270
            return $this->parseArrayWhereItems($field, $logic);
1271
        } elseif ($field instanceof Closure) {
1272
            $where = $field;
1273
        } elseif (is_string($field)) {
1274
            if (preg_match('/[,=\<\'\"\(\s]/', $field)) {
1275
                return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
1276
            } elseif (is_string($op) && strtolower($op) == 'exp') {
1277
                $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : [];
1278
                return $this->whereExp($field, $condition, $bind, $logic);
1279
            }
1280
1281
            $where = $this->parseWhereItem($logic, $field, $op, $condition, $param);
1282
        }
1283
1284
        if (!empty($where)) {
1285
            $this->options['where'][$logic][] = $where;
1286
        }
1287
1288
        return $this;
1289
    }
1290
1291
    /**
1292
     * 分析查询表达式
1293
     * @access protected
1294
     * @param string $logic     查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1295
     * @param mixed  $field     查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1296
     * @param mixed  $op        查询表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1297
     * @param mixed  $condition 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1298
     * @param array  $param     查询参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1299
     * @return array
1300
     */
1301
    protected function parseWhereItem(string $logic, $field, $op, $condition, array $param = []): array
1302
    {
1303
        if (is_array($op)) {
1304
            // 同一字段多条件查询
1305
            array_unshift($param, $field);
1306
            $where = $param;
1307
        } elseif ($field && is_null($condition)) {
1308
            if (is_string($op) && in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) {
1309
                // null查询
1310
                $where = [$field, $op, ''];
1311
            } elseif ('=' === $op || is_null($op)) {
1312
                $where = [$field, 'NULL', ''];
1313
            } elseif ('<>' === $op) {
1314
                $where = [$field, 'NOTNULL', ''];
1315
            } else {
1316
                // 字段相等查询
1317
                $where = $this->whereEq($field, $op);
1318
            }
1319
        } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
1320
            $where = [$field, $op, is_string($condition) ? new Raw($condition) : $condition];
1321
        } else {
1322
            $where = $field ? [$field, $op, $condition, $param[2] ?? null] : [];
1323
        }
1324
1325
        return $where;
1326
    }
1327
1328
    /**
1329
     * 相等查询的主键处理
1330
     * @access protected
1331
     * @param string $field 字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1332
     * @param mixed  $value 字段值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1333
     * @return array
1334
     */
1335
    protected function whereEq(string $field, $value): array
1336
    {
1337
        $where = [$field, '=', $value];
1338
        if ($this->getPk() == $field) {
1339
            $this->options['key'] = $value;
1340
        }
1341
1342
        return $where;
1343
    }
1344
1345
    /**
1346
     * 数组批量查询
1347
     * @access protected
1348
     * @param array  $field 批量查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1349
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1350
     * @return $this
1351
     */
1352
    protected function parseArrayWhereItems(array $field, string $logic)
1353
    {
1354
        if (key($field) !== 0) {
1355
            $where = [];
1356
            foreach ($field as $key => $val) {
1357
                if ($val instanceof Raw) {
1358
                    $where[] = [$key, 'exp', $val];
1359
                } else {
1360
                    $where[] = is_null($val) ? [$key, 'NULL', ''] : [$key, is_array($val) ? 'IN' : '=', $val];
1361
                }
1362
            }
1363
        } else {
1364
            // 数组批量查询
1365
            $where = $field;
1366
        }
1367
1368
        if (!empty($where)) {
1369
            $this->options['where'][$logic] = isset($this->options['where'][$logic]) ? array_merge($this->options['where'][$logic], $where) : $where;
1370
        }
1371
1372
        return $this;
1373
    }
1374
1375
    /**
1376
     * 去除某个查询条件
1377
     * @access public
1378
     * @param string $field 查询字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1379
     * @param string $logic 查询逻辑 and or xor
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1380
     * @return $this
1381
     */
1382
    public function removeWhereField(string $field, string $logic = 'AND')
1383
    {
1384
        $logic = strtoupper($logic);
1385
1386
        if (isset($this->options['where'][$logic])) {
1387
            foreach ($this->options['where'][$logic] as $key => $val) {
1388
                if (is_array($val) && $val[0] == $field) {
1389
                    unset($this->options['where'][$logic][$key]);
1390
                }
1391
            }
1392
        }
1393
1394
        return $this;
1395
    }
1396
1397
    /**
1398
     * 去除查询参数
1399
     * @access public
1400
     * @param string $option 参数名 留空去除所有参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1401
     * @return $this
1402
     */
1403
    public function removeOption(string $option = '')
1404
    {
1405
        if ('' === $option) {
1406
            $this->options = [];
1407
            $this->bind    = [];
1408
        } elseif (isset($this->options[$option])) {
1409
            unset($this->options[$option]);
1410
        }
1411
1412
        return $this;
1413
    }
1414
1415
    /**
1416
     * 条件查询
1417
     * @access public
1418
     * @param mixed         $condition 满足条件(支持闭包)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1419
     * @param Closure|array $query     满足条件后执行的查询表达式(闭包或数组)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1420
     * @param Closure|array $otherwise 不满足条件后执行
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1421
     * @return $this
1422
     */
1423
    public function when($condition, $query, $otherwise = null)
1424
    {
1425
        if ($condition instanceof Closure) {
1426
            $condition = $condition($this);
1427
        }
1428
1429
        if ($condition) {
1430
            if ($query instanceof Closure) {
1431
                $query($this, $condition);
1432
            } elseif (is_array($query)) {
0 ignored issues
show
introduced by
The condition is_array($query) is always true.
Loading history...
1433
                $this->where($query);
1434
            }
1435
        } elseif ($otherwise) {
1436
            if ($otherwise instanceof Closure) {
1437
                $otherwise($this, $condition);
1438
            } elseif (is_array($otherwise)) {
0 ignored issues
show
introduced by
The condition is_array($otherwise) is always true.
Loading history...
1439
                $this->where($otherwise);
1440
            }
1441
        }
1442
1443
        return $this;
1444
    }
1445
1446
    /**
1447
     * 指定查询数量
1448
     * @access public
1449
     * @param int $offset 起始位置
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1450
     * @param int $length 查询数量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1451
     * @return $this
1452
     */
1453
    public function limit(int $offset, int $length = null)
1454
    {
1455
        $this->options['limit'] = $offset . ($length ? ',' . $length : '');
1456
1457
        return $this;
1458
    }
1459
1460
    /**
1461
     * 指定分页
1462
     * @access public
1463
     * @param int $page     页数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1464
     * @param int $listRows 每页数量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1465
     * @return $this
1466
     */
1467
    public function page(int $page, int $listRows = null)
1468
    {
1469
        $this->options['page'] = [$page, $listRows];
1470
1471
        return $this;
1472
    }
1473
1474
    /**
1475
     * 分页查询
1476
     * @access public
1477
     * @param int|array $listRows 每页数量 数组表示配置参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1478
     * @param int|bool  $simple   是否简洁模式或者总记录数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1479
     * @param array     $config   配置参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1480
     * @return Paginator
1481
     * @throws DbException
1482
     */
1483
    public function paginate($listRows = null, $simple = false, $config = [])
1484
    {
1485
        if (is_int($simple)) {
1486
            $total  = $simple;
1487
            $simple = false;
1488
        }
1489
1490
        $defaultConfig = [
1491
            'query'     => [], //url额外参数
1492
            'fragment'  => '', //url锚点
1493
            'var_page'  => 'page', //分页变量
1494
            'list_rows' => 15, //每页数量
1495
        ];
1496
1497
        if (is_array($listRows)) {
1498
            $config   = array_merge($defaultConfig, $listRows);
1499
            $listRows = intval($config['list_rows']);
1500
        } else {
1501
            $config   = array_merge($defaultConfig, $config);
1502
            $listRows = intval($listRows ?: $config['list_rows']);
1503
        }
1504
1505
        $page = isset($config['page']) ? (int) $config['page'] : Paginator::getCurrentPage($config['var_page']);
1506
1507
        $page = $page < 1 ? 1 : $page;
1508
1509
        $config['path'] = $config['path'] ?? Paginator::getCurrentPath();
1510
1511
        if (!isset($total) && !$simple) {
1512
            $options = $this->getOptions();
1513
1514
            unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
1515
1516
            $bind    = $this->bind;
1517
            $total   = $this->count();
1518
            $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
1519
        } elseif ($simple) {
1520
            $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
1521
            $total   = null;
1522
        } else {
1523
            $results = $this->page($page, $listRows)->select();
1524
        }
1525
1526
        $this->removeOption('limit');
1527
        $this->removeOption('page');
1528
1529
        return Paginator::make($results, $listRows, $page, $total, $simple, $config);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $total does not seem to be defined for all execution paths leading up to this point.
Loading history...
1530
    }
1531
1532
    /**
1533
     * 表达式方式指定当前操作的数据表
1534
     * @access public
1535
     * @param mixed $table 表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1536
     * @return $this
1537
     */
1538
    public function tableRaw(string $table)
1539
    {
1540
        $this->options['table'] = new Raw($table);
1541
1542
        return $this;
1543
    }
1544
1545
    /**
1546
     * 指定当前操作的数据表
1547
     * @access public
1548
     * @param mixed $table 表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1549
     * @return $this
1550
     */
1551
    public function table($table)
1552
    {
1553
        if (is_string($table)) {
1554
            if (strpos($table, ')')) {
1555
                // 子查询
1556
            } elseif (false === strpos($table, ',')) {
1557
                if (strpos($table, ' ')) {
1558
                    list($item, $alias) = explode(' ', $table);
1559
                    $table              = [];
1560
                    $this->alias([$item => $alias]);
1561
                    $table[$item] = $alias;
1562
                }
1563
            } else {
1564
                $tables = explode(',', $table);
1565
                $table  = [];
1566
1567
                foreach ($tables as $item) {
1568
                    $item = trim($item);
1569
                    if (strpos($item, ' ')) {
1570
                        list($item, $alias) = explode(' ', $item);
1571
                        $this->alias([$item => $alias]);
1572
                        $table[$item] = $alias;
1573
                    } else {
1574
                        $table[] = $item;
1575
                    }
1576
                }
1577
            }
1578
        } elseif (is_array($table)) {
1579
            $tables = $table;
1580
            $table  = [];
1581
1582
            foreach ($tables as $key => $val) {
1583
                if (is_numeric($key)) {
1584
                    $table[] = $val;
1585
                } else {
1586
                    $this->alias([$key => $val]);
1587
                    $table[$key] = $val;
1588
                }
1589
            }
1590
        }
1591
1592
        $this->options['table'] = $table;
1593
1594
        return $this;
1595
    }
1596
1597
    /**
1598
     * USING支持 用于多表删除
1599
     * @access public
1600
     * @param mixed $using USING
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1601
     * @return $this
1602
     */
1603
    public function using($using)
1604
    {
1605
        $this->options['using'] = $using;
1606
        return $this;
1607
    }
1608
1609
    /**
1610
     * 存储过程调用
1611
     * @access public
1612
     * @param bool $procedure 是否为存储过程查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1613
     * @return $this
1614
     */
1615
    public function procedure($procedure = true)
1616
    {
1617
        $this->options['procedure'] = $procedure;
1618
        return $this;
1619
    }
1620
1621
    /**
1622
     * 是否允许返回空数据(或空模型)
1623
     * @access public
1624
     * @param bool $allowEmpty 是否允许为空
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1625
     * @return $this
1626
     */
1627
    public function allowEmpty(bool $allowEmpty = true)
1628
    {
1629
        $this->options['allow_empty'] = $allowEmpty;
1630
        return $this;
1631
    }
1632
1633
    /**
1634
     * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc'])
1635
     * @access public
1636
     * @param string|array|Raw $field 排序字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1637
     * @param string           $order 排序
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1638
     * @return $this
1639
     */
1640
    public function order($field, string $order = '')
1641
    {
1642
        if (empty($field)) {
1643
            return $this;
1644
        } elseif ($field instanceof Raw) {
1645
            $this->options['order'][] = $field;
1646
            return $this;
1647
        }
1648
1649
        if (is_string($field)) {
1650
            if (!empty($this->options['via'])) {
1651
                $field = $this->options['via'] . '.' . $field;
1652
            }
1653
            if (strpos($field, ',')) {
1654
                $field = array_map('trim', explode(',', $field));
1655
            } else {
1656
                $field = empty($order) ? $field : [$field => $order];
1657
            }
1658
        } elseif (!empty($this->options['via'])) {
1659
            foreach ($field as $key => $val) {
1660
                if (is_numeric($key)) {
1661
                    $field[$key] = $this->options['via'] . '.' . $val;
1662
                } else {
1663
                    $field[$this->options['via'] . '.' . $key] = $val;
1664
                    unset($field[$key]);
1665
                }
1666
            }
1667
        }
1668
1669
        if (!isset($this->options['order'])) {
1670
            $this->options['order'] = [];
1671
        }
1672
1673
        if (is_array($field)) {
1674
            $this->options['order'] = array_merge($this->options['order'], $field);
1675
        } else {
1676
            $this->options['order'][] = $field;
1677
        }
1678
1679
        return $this;
1680
    }
1681
1682
    /**
1683
     * 表达式方式指定Field排序
1684
     * @access public
1685
     * @param string $field 排序字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1686
     * @param array  $bind  参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1687
     * @return $this
1688
     */
1689
    public function orderRaw(string $field, array $bind = [])
1690
    {
1691
        if (!empty($bind)) {
1692
            $this->bindParams($field, $bind);
1693
        }
1694
1695
        $this->options['order'][] = new Raw($field);
1696
1697
        return $this;
1698
    }
1699
1700
    /**
1701
     * 指定Field排序 orderField('id',[1,2,3],'desc')
1702
     * @access public
1703
     * @param string $field  排序字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1704
     * @param array  $values 排序值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1705
     * @param string $order  排序 desc/asc
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1706
     * @return $this
1707
     */
1708
    public function orderField(string $field, array $values, string $order = '')
1709
    {
1710
        if (!empty($values)) {
1711
            $values['sort'] = $order;
1712
1713
            $this->options['order'][$field] = $values;
1714
        }
1715
1716
        return $this;
1717
    }
1718
1719
    /**
1720
     * 随机排序
1721
     * @access public
1722
     * @return $this
1723
     */
1724
    public function orderRand()
1725
    {
1726
        $this->options['order'][] = '[rand]';
1727
        return $this;
1728
    }
1729
1730
    /**
1731
     * 查询缓存
1732
     * @access public
1733
     * @param mixed             $key    缓存key
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1734
     * @param integer|\DateTime $expire 缓存有效期
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1735
     * @param string            $tag    缓存标签
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1736
     * @return $this
1737
     */
1738
    public function cache($key = true, $expire = null, $tag = null)
1739
    {
1740
        if (false === $key) {
1741
            return $this;
1742
        }
1743
1744
        if ($key instanceof \DateTimeInterface || (is_int($key) && is_null($expire))) {
1745
            $expire = $key;
1746
            $key    = true;
1747
        }
1748
1749
        $this->options['cache'] = [$key, $expire, $tag];
1750
1751
        return $this;
1752
    }
1753
1754
    /**
1755
     * 指定group查询
1756
     * @access public
1757
     * @param string|array $group GROUP
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1758
     * @return $this
1759
     */
1760
    public function group($group)
1761
    {
1762
        $this->options['group'] = $group;
1763
        return $this;
1764
    }
1765
1766
    /**
1767
     * 指定having查询
1768
     * @access public
1769
     * @param string $having having
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1770
     * @return $this
1771
     */
1772
    public function having(string $having)
1773
    {
1774
        $this->options['having'] = $having;
1775
        return $this;
1776
    }
1777
1778
    /**
1779
     * 指定查询lock
1780
     * @access public
1781
     * @param bool|string $lock 是否lock
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1782
     * @return $this
1783
     */
1784
    public function lock($lock = false)
1785
    {
1786
        $this->options['lock'] = $lock;
1787
1788
        if ($lock) {
1789
            $this->options['master'] = true;
1790
        }
1791
1792
        return $this;
1793
    }
1794
1795
    /**
1796
     * 指定distinct查询
1797
     * @access public
1798
     * @param bool $distinct 是否唯一
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1799
     * @return $this
1800
     */
1801
    public function distinct(bool $distinct = true)
1802
    {
1803
        $this->options['distinct'] = $distinct;
1804
        return $this;
1805
    }
1806
1807
    /**
1808
     * 指定数据表别名
1809
     * @access public
1810
     * @param array|string $alias 数据表别名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1811
     * @return $this
1812
     */
1813
    public function alias($alias)
1814
    {
1815
        if (is_array($alias)) {
1816
            $this->options['alias'] = $alias;
1817
        } else {
1818
            $table = $this->getTable();
1819
1820
            $this->options['alias'][$table] = $alias;
1821
        }
1822
1823
        return $this;
1824
    }
1825
1826
    /**
1827
     * 指定强制索引
1828
     * @access public
1829
     * @param string $force 索引名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1830
     * @return $this
1831
     */
1832
    public function force(string $force)
1833
    {
1834
        $this->options['force'] = $force;
1835
        return $this;
1836
    }
1837
1838
    /**
1839
     * 查询注释
1840
     * @access public
1841
     * @param string $comment 注释
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1842
     * @return $this
1843
     */
1844
    public function comment(string $comment)
1845
    {
1846
        $this->options['comment'] = $comment;
1847
        return $this;
1848
    }
1849
1850
    /**
1851
     * 获取执行的SQL语句而不进行实际的查询
1852
     * @access public
1853
     * @param bool $fetch 是否返回sql
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1854
     * @return $this|Fetch
1855
     */
1856
    public function fetchSql(bool $fetch = true)
1857
    {
1858
        $this->options['fetch_sql'] = $fetch;
1859
1860
        if ($fetch) {
1861
            return new Fetch($this);
1862
        }
1863
1864
        return $this;
1865
    }
1866
1867
    /**
1868
     * 设置是否返回数据集对象
1869
     * @access public
1870
     * @param bool|string $collection 是否返回数据集对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1871
     * @return $this
1872
     */
1873
    public function fetchCollection($collection = true)
1874
    {
1875
        $this->options['collection'] = $collection;
1876
        return $this;
1877
    }
1878
1879
    /**
1880
     * 设置是否返回数组
1881
     * @access public
1882
     * @param bool $asArray 是否返回数组
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1883
     * @return $this
1884
     */
1885
    public function fetchArray(bool $asArray = true)
1886
    {
1887
        $this->options['array'] = $asArray;
1888
        return $this;
1889
    }
1890
1891
    /**
1892
     * 设置从主服务器读取数据
1893
     * @access public
1894
     * @param bool $readMaster 是否从主服务器读取
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1895
     * @return $this
1896
     */
1897
    public function master(bool $readMaster = true)
1898
    {
1899
        $this->options['master'] = $readMaster;
1900
        return $this;
1901
    }
1902
1903
    /**
1904
     * 设置是否严格检查字段名
1905
     * @access public
1906
     * @param bool $strict 是否严格检查字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1907
     * @return $this
1908
     */
1909
    public function strict(bool $strict = true)
1910
    {
1911
        $this->options['strict'] = $strict;
1912
        return $this;
1913
    }
1914
1915
    /**
1916
     * 设置查询数据不存在是否抛出异常
1917
     * @access public
1918
     * @param bool $fail 数据不存在是否抛出异常
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1919
     * @return $this
1920
     */
1921
    public function failException(bool $fail = true)
1922
    {
1923
        $this->options['fail'] = $fail;
1924
        return $this;
1925
    }
1926
1927
    /**
1928
     * 设置自增序列名
1929
     * @access public
1930
     * @param string $sequence 自增序列名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1931
     * @return $this
1932
     */
1933
    public function sequence(string $sequence = null)
1934
    {
1935
        $this->options['sequence'] = $sequence;
1936
        return $this;
1937
    }
1938
1939
    /**
1940
     * 设置是否REPLACE
1941
     * @access public
1942
     * @param bool $replace 是否使用REPLACE写入数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1943
     * @return $this
1944
     */
1945
    public function replace(bool $replace = true)
1946
    {
1947
        $this->options['replace'] = $replace;
1948
        return $this;
1949
    }
1950
1951
    /**
1952
     * 设置当前查询所在的分区
1953
     * @access public
1954
     * @param string|array $partition 分区名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1955
     * @return $this
1956
     */
1957
    public function partition($partition)
1958
    {
1959
        $this->options['partition'] = $partition;
1960
        return $this;
1961
    }
1962
1963
    /**
1964
     * 设置DUPLICATE
1965
     * @access public
1966
     * @param array|string|Raw $duplicate DUPLICATE信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1967
     * @return $this
1968
     */
1969
    public function duplicate($duplicate)
1970
    {
1971
        $this->options['duplicate'] = $duplicate;
1972
        return $this;
1973
    }
1974
1975
    /**
1976
     * 设置查询的额外参数
1977
     * @access public
1978
     * @param string $extra 额外信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1979
     * @return $this
1980
     */
1981
    public function extra(string $extra)
1982
    {
1983
        $this->options['extra'] = $extra;
1984
        return $this;
1985
    }
1986
1987
    /**
1988
     * 设置需要隐藏的输出属性
1989
     * @access public
1990
     * @param array $hidden 需要隐藏的字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1991
     * @return $this
1992
     */
1993
    public function hidden(array $hidden)
1994
    {
1995
        $this->options['hidden'] = $hidden;
1996
        return $this;
1997
    }
1998
1999
    /**
2000
     * 设置需要输出的属性
2001
     * @access public
2002
     * @param array $visible 需要输出的属性
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2003
     * @return $this
2004
     */
2005
    public function visible(array $visible)
2006
    {
2007
        $this->options['visible'] = $visible;
2008
        return $this;
2009
    }
2010
2011
    /**
2012
     * 设置需要追加输出的属性
2013
     * @access public
2014
     * @param array $append 需要追加的属性
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2015
     * @return $this
2016
     */
2017
    public function append(array $append)
2018
    {
2019
        $this->options['append'] = $append;
2020
        return $this;
2021
    }
2022
2023
    /**
2024
     * 设置JSON字段信息
2025
     * @access public
2026
     * @param array $json  JSON字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2027
     * @param bool  $assoc 是否取出数组
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2028
     * @return $this
2029
     */
2030
    public function json(array $json = [], bool $assoc = false)
2031
    {
2032
        $this->options['json']       = $json;
2033
        $this->options['json_assoc'] = $assoc;
2034
        return $this;
2035
    }
2036
2037
    /**
0 ignored issues
show
Coding Style introduced by
Parameter ...$args should have a doc-comment as per coding-style.
Loading history...
2038
     * 添加查询范围
2039
     * @access public
2040
     * @param array|string|Closure $scope 查询范围定义
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2041
     * @param array                $args  参数
1 ignored issue
show
Coding Style introduced by
Doc comment for parameter $args does not match actual variable name ...$args
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2042
     * @return $this
2043
     */
2044
    public function scope($scope, ...$args)
2045
    {
2046
        // 查询范围的第一个参数始终是当前查询对象
2047
        array_unshift($args, $this);
2048
2049
        if ($scope instanceof Closure) {
2050
            call_user_func_array($scope, $args);
2051
            return $this;
2052
        }
2053
2054
        if (is_string($scope)) {
2055
            $scope = explode(',', $scope);
2056
        }
2057
2058
        if ($this->model) {
2059
            // 检查模型类的查询范围方法
2060
            foreach ($scope as $name) {
2061
                $method = 'scope' . trim($name);
2062
2063
                if (method_exists($this->model, $method)) {
2064
                    call_user_func_array([$this->model, $method], $args);
2065
                }
2066
            }
2067
        }
2068
2069
        return $this;
2070
    }
2071
2072
    /**
2073
     * 指定数据表主键
2074
     * @access public
2075
     * @param string $pk 主键
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2076
     * @return $this
2077
     */
2078
    public function pk(string $pk)
2079
    {
2080
        $this->pk = $pk;
2081
        return $this;
2082
    }
2083
2084
    /**
2085
     * 添加日期或者时间查询规则
2086
     * @access public
2087
     * @param string       $name 时间表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2088
     * @param string|array $rule 时间范围
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2089
     * @return $this
2090
     */
2091
    public function timeRule(string $name, $rule)
2092
    {
2093
        $this->timeRule[$name] = $rule;
2094
        return $this;
2095
    }
2096
2097
    /**
2098
     * 查询日期或者时间
2099
     * @access public
2100
     * @param string       $field 日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2101
     * @param string       $op    比较运算符或者表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2102
     * @param string|array $range 比较范围
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2103
     * @param string       $logic AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2104
     * @return $this
2105
     */
2106
    public function whereTime(string $field, string $op, $range = null, string $logic = 'AND')
2107
    {
2108
        if (is_null($range) && isset($this->timeRule[$op])) {
2109
            $range = $this->timeRule[$op];
2110
            $op    = 'between';
2111
        }
2112
2113
        return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true);
2114
    }
2115
2116
    /**
2117
     * 查询某个时间间隔数据
2118
     * @access protected
2119
     * @param string $field    日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2120
     * @param string $start    开始时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2121
     * @param string $interval 时间间隔单位
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2122
     * @param string $logic    AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2123
     * @return $this
2124
     */
2125
    protected function whereTimeInterval(string $field, string $start, string $interval = 'day', string $logic = 'AND')
2126
    {
2127
        $startTime = strtotime($start);
2128
        $endTime   = strtotime('+1 ' . $interval, $startTime);
2129
2130
        return $this->whereTime($field, 'between', [$startTime, $endTime], $logic);
2131
    }
2132
2133
    /**
2134
     * 查询月数据 whereMonth('time_field', '2018-1')
2135
     * @access public
2136
     * @param string $field 日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2137
     * @param string $month 月份信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2138
     * @param string $logic AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2139
     * @return $this
2140
     */
2141
    public function whereMonth(string $field, string $month = 'this month', string $logic = 'AND')
2142
    {
2143
        if (in_array($month, ['this month', 'last month'])) {
2144
            $month = date('Y-m', strtotime($month));
2145
        }
2146
2147
        return $this->whereTimeInterval($field, $month, 'month', $logic);
2148
    }
2149
2150
    /**
2151
     * 查询年数据 whereYear('time_field', '2018')
2152
     * @access public
2153
     * @param string $field 日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2154
     * @param string $year  年份信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2155
     * @param string $logic AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2156
     * @return $this
2157
     */
2158
    public function whereYear(string $field, string $year = 'this year', string $logic = 'AND')
2159
    {
2160
        if (in_array($year, ['this year', 'last year'])) {
2161
            $year = date('Y', strtotime($year));
2162
        }
2163
2164
        return $this->whereTimeInterval($field, $year . '-1-1', 'year', $logic);
2165
    }
2166
2167
    /**
2168
     * 查询日数据 whereDay('time_field', '2018-1-1')
2169
     * @access public
2170
     * @param string $field 日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2171
     * @param string $day   日期信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2172
     * @param string $logic AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2173
     * @return $this
2174
     */
2175
    public function whereDay(string $field, string $day = 'today', string $logic = 'AND')
2176
    {
2177
        if (in_array($day, ['today', 'yesterday'])) {
2178
            $day = date('Y-m-d', strtotime($day));
2179
        }
2180
2181
        return $this->whereTimeInterval($field, $day, 'day', $logic);
2182
    }
2183
2184
    /**
2185
     * 查询日期或者时间范围 whereBetweenTime('time_field', '2018-1-1','2018-1-15')
2186
     * @access public
2187
     * @param string     $field     日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2188
     * @param string|int $startTime 开始时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2189
     * @param string|int $endTime   结束时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2190
     * @param string     $logic     AND OR
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2191
     * @return $this
2192
     */
2193
    public function whereBetweenTime(string $field, $startTime, $endTime, string $logic = 'AND')
2194
    {
2195
        return $this->whereTime($field, 'between', [$startTime, $endTime], $logic);
2196
    }
2197
2198
    /**
2199
     * 查询日期或者时间范围 whereNotBetweenTime('time_field', '2018-1-1','2018-1-15')
2200
     * @access public
2201
     * @param string     $field     日期字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2202
     * @param string|int $startTime 开始时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2203
     * @param string|int $endTime   结束时间
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2204
     * @return $this
2205
     */
2206
    public function whereNotBetweenTime(string $field, $startTime, $endTime)
2207
    {
2208
        return $this->whereTime($field, '<', $startTime)
2209
            ->whereTime($field, '>', $endTime);
2210
    }
2211
2212
    /**
2213
     * 查询当前时间在两个时间字段范围 whereBetweenTimeField('start_time', 'end_time')
2214
     * @access public
2215
     * @param string $startField 开始时间字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2216
     * @param string $endField   结束时间字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2217
     * @return $this
2218
     */
2219
    public function whereBetweenTimeField(string $startField, string $endField)
2220
    {
2221
        return $this->whereTime($startField, '<=', time())
2222
            ->whereTime($endField, '>=', time());
2223
    }
2224
2225
    /**
2226
     * 查询当前时间不在两个时间字段范围 whereNotBetweenTimeField('start_time', 'end_time')
2227
     * @access public
2228
     * @param string $startField 开始时间字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2229
     * @param string $endField   结束时间字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2230
     * @return $this
2231
     */
2232
    public function whereNotBetweenTimeField(string $startField, string $endField)
2233
    {
2234
        return $this->whereTime($startField, '>', time())
2235
            ->whereTime($endField, '<', time(), 'OR');
2236
    }
2237
2238
    /**
2239
     * 获取当前数据表的主键
2240
     * @access public
2241
     * @return string|array
2242
     */
2243
    public function getPk()
2244
    {
2245
        if (!empty($this->pk)) {
2246
            $pk = $this->pk;
2247
        } else {
2248
            $this->pk = $pk = $this->connection->getPk($this->getTable());
2249
        }
2250
2251
        return $pk;
2252
    }
2253
2254
    /**
2255
     * 批量参数绑定
2256
     * @access public
2257
     * @param array $value 绑定变量值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2258
     * @return $this
2259
     */
2260
    public function bind(array $value)
2261
    {
2262
        $this->bind = array_merge($this->bind, $value);
2263
        return $this;
2264
    }
2265
2266
    /**
2267
     * 单个参数绑定
2268
     * @access public
2269
     * @param mixed   $value 绑定变量值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2270
     * @param integer $type  绑定类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2271
     * @param string  $name  绑定标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2272
     * @return string
2273
     */
2274
    public function bindValue($value, int $type = null, string $name = null)
2275
    {
2276
        $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_';
2277
2278
        $this->bind[$name] = [$value, $type ?: PDO::PARAM_STR];
2279
        return $name;
2280
    }
2281
2282
    /**
2283
     * 检测参数是否已经绑定
2284
     * @access public
2285
     * @param string $key 参数名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2286
     * @return bool
2287
     */
2288
    public function isBind($key)
2289
    {
2290
        return isset($this->bind[$key]);
2291
    }
2292
2293
    /**
2294
     * 参数绑定
2295
     * @access public
2296
     * @param string $sql  绑定的sql表达式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2297
     * @param array  $bind 参数绑定
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2298
     * @return void
2299
     */
2300
    protected function bindParams(string &$sql, array $bind = []): void
2301
    {
2302
        foreach ($bind as $key => $value) {
2303
            if (is_array($value)) {
2304
                $name = $this->bindValue($value[0], $value[1], $value[2] ?? null);
2305
            } else {
2306
                $name = $this->bindValue($value);
2307
            }
2308
2309
            if (is_numeric($key)) {
2310
                $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1);
2311
            } else {
2312
                $sql = str_replace(':' . $key, ':' . $name, $sql);
2313
            }
2314
        }
2315
    }
2316
2317
    /**
2318
     * 查询参数批量赋值
2319
     * @access protected
2320
     * @param array $options 表达式参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2321
     * @return $this
2322
     */
2323
    protected function options(array $options)
2324
    {
2325
        $this->options = $options;
2326
        return $this;
2327
    }
2328
2329
    /**
2330
     * 获取当前的查询参数
2331
     * @access public
2332
     * @param string $name 参数名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2333
     * @return mixed
2334
     */
2335
    public function getOptions(string $name = '')
2336
    {
2337
        if ('' === $name) {
2338
            return $this->options;
2339
        }
2340
2341
        return $this->options[$name] ?? null;
2342
    }
2343
2344
    /**
2345
     * 设置当前的查询参数
2346
     * @access public
2347
     * @param string $option 参数名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2348
     * @param mixed  $value  参数值
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2349
     * @return $this
2350
     */
2351
    public function setOption(string $option, $value)
2352
    {
2353
        $this->options[$option] = $value;
2354
        return $this;
2355
    }
2356
2357
    /**
2358
     * 设置关联查询
2359
     * @access public
2360
     * @param array $relation 关联名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2361
     * @return $this
2362
     */
2363
    public function relation(array $relation)
2364
    {
2365
        if (!empty($relation)) {
2366
            $this->options['relation'] = $relation;
2367
        }
2368
2369
        return $this;
2370
    }
2371
2372
    /**
2373
     * 设置关联查询JOIN预查询
2374
     * @access public
2375
     * @param array $with 关联方法名称(数组)
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2376
     * @return $this
2377
     */
2378
    public function with(array $with)
2379
    {
2380
        if (!empty($with)) {
2381
            $this->options['with'] = $with;
2382
        }
2383
2384
        return $this;
2385
    }
2386
2387
    /**
2388
     * 关联预载入 JOIN方式
2389
     * @access protected
2390
     * @param array  $with     关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2391
     * @param string $joinType JOIN方式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2392
     * @return $this
2393
     */
2394
    public function withJoin(array $with, string $joinType = '')
2395
    {
2396
        if (empty($with)) {
2397
            return $this;
2398
        }
2399
2400
        $first = true;
2401
2402
        /** @var Model $class */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
2403
        $class = $this->model;
2404
        foreach ($with as $key => $relation) {
2405
            $closure = null;
2406
            $field   = true;
2407
2408
            if ($relation instanceof Closure) {
2409
                // 支持闭包查询过滤关联条件
2410
                $closure  = $relation;
2411
                $relation = $key;
2412
            } elseif (is_array($relation)) {
2413
                $field    = $relation;
2414
                $relation = $key;
2415
            } elseif (is_string($relation) && strpos($relation, '.')) {
2416
                $relation = strstr($relation, '.', true);
2417
            }
2418
2419
            /** @var Relation $model */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
2420
            $relation = App::parseName($relation, 1, false);
2421
            $model    = $class->$relation();
2422
2423
            if ($model instanceof OneToOne) {
2424
                $model->eagerly($this, $relation, $field, $joinType, $closure, $first);
2425
                $first = false;
2426
            } else {
2427
                // 不支持其它关联
2428
                unset($with[$key]);
2429
            }
2430
        }
2431
2432
        $this->via();
2433
2434
        $this->options['with_join'] = $with;
2435
2436
        return $this;
2437
    }
2438
2439
    /**
2440
     * 设置数据字段获取器
2441
     * @access public
2442
     * @param string   $name     字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2443
     * @param callable $callback 闭包获取器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2444
     * @return $this
2445
     */
2446
    public function withAttr(string $name, callable $callback)
2447
    {
2448
        $this->options['with_attr'][$name] = $callback;
2449
2450
        return $this;
2451
    }
2452
2453
    /**
2454
     * 设置数据字段获取器
2455
     * @access public
2456
     * @param array $attrs 字段获取器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2457
     * @return $this
2458
     */
2459
    public function withAttrs(array $attrs)
2460
    {
2461
        $this->options['with_attr'] = $attrs;
2462
2463
        return $this;
2464
    }
2465
2466
    /**
2467
     * 使用搜索器条件搜索字段
2468
     * @access public
2469
     * @param array  $fields 搜索字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2470
     * @param array  $data   搜索数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2471
     * @param string $prefix 字段前缀标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2472
     * @return $this
2473
     */
2474
    public function withSearch(array $fields, array $data = [], string $prefix = '')
2475
    {
2476
        foreach ($fields as $key => $field) {
2477
            if ($field instanceof Closure) {
2478
                $field($this, $data[$key] ?? null, $data, $prefix);
2479
            } elseif ($this->model) {
2480
                // 检测搜索器
2481
                $fieldName = is_numeric($key) ? $field : $key;
2482
                $method    = 'search' . App::parseName($fieldName, 1) . 'Attr';
2483
2484
                if (method_exists($this->model, $method)) {
2485
                    $this->model->$method($this, $data[$field] ?? null, $data, $prefix);
2486
                }
2487
            }
2488
        }
2489
2490
        return $this;
2491
    }
2492
2493
    /**
2494
     * 关联统计
2495
     * @access protected
2496
     * @param array|string $relations 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2497
     * @param string       $aggregate 聚合查询方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2498
     * @param string       $field     字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2499
     * @param bool         $subQuery  是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2500
     * @return $this
2501
     */
2502
    protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true)
2503
    {
2504
        if (is_string($relations)) {
2505
            $relations = explode(',', $relations);
2506
        }
2507
2508
        if (!$subQuery) {
2509
            $this->options['with_count'][] = [$relations, $aggregate, $field];
2510
        } else {
2511
            if (!isset($this->options['field'])) {
2512
                $this->field('*');
2513
            }
2514
2515
            foreach ($relations as $key => $relation) {
2516
                $closure = $aggregateField = null;
2517
2518
                if ($relation instanceof Closure) {
2519
                    $closure  = $relation;
2520
                    $relation = $key;
2521
                } elseif (!is_int($key)) {
2522
                    $aggregateField = $relation;
2523
                    $relation       = $key;
2524
                }
2525
2526
                $relation = App::parseName($relation, 1, false);
2527
2528
                $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField) . ')';
2529
2530
                if (empty($aggregateField)) {
2531
                    $aggregateField = App::parseName($relation) . '_' . $aggregate;
2532
                }
2533
2534
                $this->field([$count => $aggregateField]);
2535
            }
2536
        }
2537
2538
        return $this;
2539
    }
2540
2541
    /**
2542
     * 关联统计
2543
     * @access public
2544
     * @param string|array $relation 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2545
     * @param bool         $subQuery 是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2546
     * @return $this
2547
     */
2548
    public function withCount($relation, bool $subQuery = true)
2549
    {
2550
        return $this->withAggregate($relation, 'count', '*', $subQuery);
2551
    }
2552
2553
    /**
2554
     * 关联统计Sum
2555
     * @access public
2556
     * @param string|array $relation 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2557
     * @param string       $field    字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2558
     * @param bool         $subQuery 是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2559
     * @return $this
2560
     */
2561
    public function withSum($relation, string $field, bool $subQuery = true)
2562
    {
2563
        return $this->withAggregate($relation, 'sum', $field, $subQuery);
2564
    }
2565
2566
    /**
2567
     * 关联统计Max
2568
     * @access public
2569
     * @param string|array $relation 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2570
     * @param string       $field    字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2571
     * @param bool         $subQuery 是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2572
     * @return $this
2573
     */
2574
    public function withMax($relation, string $field, bool $subQuery = true)
2575
    {
2576
        return $this->withAggregate($relation, 'max', $field, $subQuery);
2577
    }
2578
2579
    /**
2580
     * 关联统计Min
2581
     * @access public
2582
     * @param string|array $relation 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2583
     * @param string       $field    字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2584
     * @param bool         $subQuery 是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2585
     * @return $this
2586
     */
2587
    public function withMin($relation, string $field, bool $subQuery = true)
2588
    {
2589
        return $this->withAggregate($relation, 'min', $field, $subQuery);
2590
    }
2591
2592
    /**
2593
     * 关联统计Avg
2594
     * @access public
2595
     * @param string|array $relation 关联方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2596
     * @param string       $field    字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2597
     * @param bool         $subQuery 是否使用子查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2598
     * @return $this
2599
     */
2600
    public function withAvg($relation, string $field, bool $subQuery = true)
2601
    {
2602
        return $this->withAggregate($relation, 'avg', $field, $subQuery);
2603
    }
2604
2605
    /**
2606
     * 关联预加载中 获取关联指定字段值
2607
     * example:
2608
     * Model::with(['relation' => function($query){
2609
     *     $query->withField("id,name");
2610
     * }])
2611
     *
2612
     * @access public
2613
     * @param string|array $field 指定获取的字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2614
     * @return $this
2615
     */
2616
    public function withField($field)
2617
    {
2618
        $this->options['with_field'] = $field;
2619
2620
        return $this;
2621
    }
2622
2623
    /**
2624
     * 设置当前字段添加的表别名
2625
     * @access public
2626
     * @param string $via 临时表别名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2627
     * @return $this
2628
     */
2629
    public function via(string $via = '')
2630
    {
2631
        $this->options['via'] = $via;
2632
2633
        return $this;
2634
    }
2635
2636
    /**
2637
     * 保存记录 自动判断insert或者update
2638
     * @access public
2639
     * @param array $data        数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2640
     * @param bool  $forceInsert 是否强制insert
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2641
     * @return integer
2642
     */
2643
    public function save(array $data = [], bool $forceInsert = false)
2644
    {
2645
        if ($forceInsert) {
2646
            return $this->insert($data);
2647
        }
2648
2649
        $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
2650
2651
        if (!empty($this->options['where'])) {
2652
            $isUpdate = true;
2653
        } else {
2654
            $isUpdate = $this->parseUpdateData($this->options['data']);
2655
        }
2656
2657
        return $isUpdate ? $this->update() : $this->insert();
2658
    }
2659
2660
    /**
2661
     * 插入记录
2662
     * @access public
2663
     * @param array   $data         数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2664
     * @param boolean $getLastInsID 返回自增主键
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2665
     * @return integer
2666
     */
2667
    public function insert(array $data = [], bool $getLastInsID = false)
2668
    {
2669
        if (!empty($data)) {
2670
            $this->options['data'] = $data;
2671
        }
2672
2673
        return $this->connection->insert($this, $getLastInsID);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connection...t($this, $getLastInsID) also could return the type string which is incompatible with the documented return type integer.
Loading history...
2674
    }
2675
2676
    /**
2677
     * 插入记录并获取自增ID
2678
     * @access public
2679
     * @param array $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2680
     * @return integer|string
2681
     */
2682
    public function insertGetId(array $data)
2683
    {
2684
        return $this->insert($data, true);
2685
    }
2686
2687
    /**
2688
     * 批量插入记录
2689
     * @access public
2690
     * @param array   $dataSet 数据集
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2691
     * @param integer $limit   每次写入数据限制
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2692
     * @return integer
2693
     */
2694
    public function insertAll(array $dataSet = [], int $limit = 0): int
2695
    {
2696
        if (empty($dataSet)) {
2697
            $dataSet = $this->options['data'] ?? [];
2698
        }
2699
2700
        if (empty($limit) && !empty($this->options['limit']) && is_numeric($this->options['limit'])) {
2701
            $limit = (int) $this->options['limit'];
2702
        }
2703
2704
        return $this->connection->insertAll($this, $dataSet, $limit);
2705
    }
2706
2707
    /**
2708
     * 通过Select方式插入记录
2709
     * @access public
2710
     * @param array  $fields 要插入的数据表字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2711
     * @param string $table  要插入的数据表名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2712
     * @return integer
2713
     * @throws PDOException
2714
     */
2715
    public function selectInsert(array $fields, string $table): int
2716
    {
2717
        return $this->connection->selectInsert($this, $fields, $table);
2718
    }
2719
2720
    /**
2721
     * 更新记录
2722
     * @access public
2723
     * @param mixed $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2724
     * @return integer
2725
     * @throws Exception
2726
     * @throws PDOException
2727
     */
2728
    public function update(array $data = []): int
2729
    {
2730
        if (!empty($data)) {
2731
            $this->options['data'] = array_merge($this->options['data'] ?? [], $data);
2732
        }
2733
2734
        if (empty($this->options['where'])) {
2735
            $this->parseUpdateData($this->options['data']);
2736
        }
2737
2738
        if (empty($this->options['where']) && $this->model) {
2739
            $this->where($this->model->getWhere());
2740
        }
2741
2742
        if (empty($this->options['where'])) {
2743
            // 如果没有任何更新条件则不执行
2744
            throw new Exception('miss update condition');
2745
        }
2746
2747
        return $this->connection->update($this);
2748
    }
2749
2750
    /**
2751
     * 删除记录
2752
     * @access public
2753
     * @param mixed $data 表达式 true 表示强制删除
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2754
     * @return int
2755
     * @throws Exception
2756
     * @throws PDOException
2757
     */
2758
    public function delete($data = null): int
2759
    {
2760
        if (!is_null($data) && true !== $data) {
2761
            // AR模式分析主键条件
2762
            $this->parsePkWhere($data);
2763
        }
2764
2765
        if (empty($this->options['where']) && $this->model) {
2766
            $this->where($this->model->getWhere());
2767
        }
2768
2769
        if (true !== $data && empty($this->options['where'])) {
2770
            // 如果条件为空 不进行删除操作 除非设置 1=1
2771
            throw new Exception('delete without condition');
2772
        }
2773
2774
        if (!empty($this->options['soft_delete'])) {
2775
            // 软删除
2776
            list($field, $condition) = $this->options['soft_delete'];
2777
            if ($condition) {
2778
                unset($this->options['soft_delete']);
2779
                $this->options['data'] = [$field => $condition];
2780
2781
                return $this->connection->update($this);
2782
            }
2783
        }
2784
2785
        $this->options['data'] = $data;
2786
2787
        return $this->connection->delete($this);
2788
    }
2789
2790
    /**
2791
     * 执行查询但只返回PDOStatement对象
2792
     * @access public
2793
     * @return PDOStatement
2794
     */
2795
    public function getPdo(): PDOStatement
2796
    {
2797
        return $this->connection->pdo($this);
2798
    }
2799
2800
    /**
2801
     * 使用游标查找记录
2802
     * @access public
2803
     * @param mixed $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2804
     * @return \Generator
2805
     */
2806
    public function cursor($data = null)
2807
    {
2808
        if (!is_null($data)) {
2809
            // 主键条件分析
2810
            $this->parsePkWhere($data);
2811
        }
2812
2813
        $this->options['data'] = $data;
2814
2815
        $connection = clone $this->connection;
2816
2817
        return $connection->cursor($this);
2818
    }
2819
2820
    /**
2821
     * 查找记录
2822
     * @access public
2823
     * @param mixed $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2824
     * @return Collection|array|ModelCollection
2825
     * @throws DbException
2826
     * @throws ModelNotFoundException
2827
     * @throws DataNotFoundException
2828
     */
2829
    public function select($data = null)
2830
    {
2831
        if (!is_null($data)) {
2832
            // 主键条件分析
2833
            $this->parsePkWhere($data);
2834
        }
2835
2836
        $resultSet = $this->connection->select($this);
2837
2838
        // 返回结果处理
2839
        if (!empty($this->options['fail']) && count($resultSet) == 0) {
2840
            $this->throwNotFound();
2841
        }
2842
2843
        // 数据列表读取后的处理
2844
        if (!empty($this->model) && empty($this->options['array'])) {
2845
            // 生成模型对象
2846
            $resultSet = $this->resultSetToModelCollection($resultSet);
2847
        } else {
2848
            $this->resultSet($resultSet);
2849
        }
2850
2851
        return $resultSet;
2852
    }
2853
2854
    /**
2855
     * 查询数据转换为模型数据集对象
2856
     * @access protected
2857
     * @param array $resultSet 数据集
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2858
     * @return ModelCollection
2859
     */
2860
    protected function resultSetToModelCollection(array $resultSet): ModelCollection
2861
    {
2862
        if (!empty($this->options['collection']) && is_string($this->options['collection'])) {
2863
            $collection = $this->options['collection'];
2864
        }
2865
2866
        if (empty($resultSet)) {
2867
            return $this->model->toCollection([], $collection ?? null);
2868
        }
2869
2870
        // 检查动态获取器
2871
        if (!empty($this->options['with_attr'])) {
2872
            foreach ($this->options['with_attr'] as $name => $val) {
2873
                if (strpos($name, '.')) {
2874
                    list($relation, $field) = explode('.', $name);
2875
2876
                    $withRelationAttr[$relation][$field] = $val;
2877
                    unset($this->options['with_attr'][$name]);
2878
                }
2879
            }
2880
        }
2881
2882
        $withRelationAttr = $withRelationAttr ?? [];
2883
2884
        foreach ($resultSet as $key => &$result) {
2885
            // 数据转换为模型对象
2886
            $this->resultToModel($result, $this->options, true, $withRelationAttr);
2887
        }
2888
2889
        if (!empty($this->options['with'])) {
2890
            // 预载入
2891
            $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result seems to be defined by a foreach iteration on line 2884. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2892
        }
2893
2894
        if (!empty($this->options['with_join'])) {
2895
            // 预载入
2896
            $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true);
2897
        }
2898
2899
        // 模型数据集转换
2900
        return $this->model->toCollection($resultSet, $collection ?? null);
2901
    }
2902
2903
    /**
2904
     * 处理数据集
2905
     * @access public
2906
     * @param array $resultSet 数据集
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2907
     * @return void
2908
     */
2909
    protected function resultSet(array &$resultSet): void
2910
    {
2911
        if (!empty($this->options['json'])) {
2912
            foreach ($resultSet as &$result) {
2913
                $this->jsonResult($result, $this->options['json'], true);
2914
            }
2915
        }
2916
2917
        if (!empty($this->options['with_attr'])) {
2918
            foreach ($resultSet as &$result) {
2919
                $this->getResultAttr($result, $this->options['with_attr']);
2920
            }
2921
        }
2922
2923
        if (!empty($this->options['visible']) || !empty($this->options['hidden'])) {
2924
            foreach ($resultSet as &$result) {
2925
                $this->filterResult($result);
2926
            }
2927
        }
2928
2929
        if (!empty($this->options['collection'])) {
2930
            // 返回Collection对象
2931
            $resultSet = new Collection($resultSet);
2932
        }
2933
    }
2934
2935
    /**
2936
     * 查找单条记录
2937
     * @access public
2938
     * @param mixed $data 查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2939
     * @return array|Model|null
2940
     * @throws DbException
2941
     * @throws ModelNotFoundException
2942
     * @throws DataNotFoundException
2943
     */
2944
    public function find($data = null)
2945
    {
2946
        if (!is_null($data)) {
2947
            // AR模式分析主键条件
2948
            $this->parsePkWhere($data);
2949
        }
2950
2951
        $result = $this->connection->find($this);
2952
2953
        // 数据处理
2954
        if (empty($result)) {
2955
            return $this->resultToEmpty();
2956
        }
2957
2958
        if (!empty($this->model) && empty($this->options['array'])) {
2959
            // 返回模型对象
2960
            $this->resultToModel($result, $this->options);
2961
        } else {
2962
            $this->result($result);
2963
        }
2964
2965
        return $result;
2966
    }
2967
2968
    /**
2969
     * 查找单条记录 不存在返回空数据(或者空模型)
2970
     * @access public
2971
     * @param mixed $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
2972
     * @return array|Model
2973
     */
2974
    public function findOrEmpty($data = null)
2975
    {
2976
        return $this->allowEmpty(true)->find($data);
2977
    }
2978
2979
    /**
2980
     * 处理空数据
2981
     * @access protected
2982
     * @return array|Model|null
2983
     * @throws DbException
2984
     * @throws ModelNotFoundException
2985
     * @throws DataNotFoundException
2986
     */
2987
    protected function resultToEmpty()
2988
    {
2989
        if (!empty($this->options['fail'])) {
2990
            $this->throwNotFound();
2991
        } elseif (!empty($this->options['allow_empty'])) {
2992
            return !empty($this->model) && empty($this->options['array']) ? $this->model->newInstance() : [];
2993
        } elseif (!empty($this->options['array'])) {
2994
            return [];
2995
        }
2996
    }
2997
2998
    /**
2999
     * 获取模型的更新条件
3000
     * @access protected
3001
     * @param array $options 查询参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3002
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
3003
    protected function getModelUpdateCondition(array $options)
3004
    {
3005
        return $options['where']['AND'] ?? null;
3006
    }
3007
3008
    /**
3009
     * 处理数据
3010
     * @access protected
3011
     * @param array $result 查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3012
     * @return void
3013
     */
3014
    protected function result(array &$result): void
3015
    {
3016
        if (!empty($this->options['json'])) {
3017
            $this->jsonResult($result, $this->options['json'], true);
3018
        }
3019
3020
        if (!empty($this->options['with_attr'])) {
3021
            $this->getResultAttr($result, $this->options['with_attr']);
3022
        }
3023
3024
        $this->filterResult($result);
3025
    }
3026
3027
    /**
3028
     * 处理数据的可见和隐藏
3029
     * @access protected
3030
     * @param array $result 查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3031
     * @return void
3032
     */
3033
    protected function filterResult(&$result): void
3034
    {
3035
        if (!empty($this->options['visible'])) {
3036
            foreach ($this->options['visible'] as $key) {
3037
                $array[] = $key;
3038
            }
3039
            $result = array_intersect_key($result, array_flip($array));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $array seems to be defined by a foreach iteration on line 3036. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
3040
        } elseif (!empty($this->options['hidden'])) {
3041
            foreach ($this->options['hidden'] as $key) {
3042
                $array[] = $key;
3043
            }
3044
            $result = array_diff_key($result, array_flip($array));
3045
        }
3046
    }
3047
3048
    /**
3049
     * 使用获取器处理数据
3050
     * @access protected
3051
     * @param array $result   查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3052
     * @param array $withAttr 字段获取器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3053
     * @return void
3054
     */
3055
    protected function getResultAttr(array &$result, array $withAttr = []): void
3056
    {
3057
        foreach ($withAttr as $name => $closure) {
3058
            $name = App::parseName($name);
3059
3060
            if (strpos($name, '.')) {
3061
                // 支持JSON字段 获取器定义
3062
                list($key, $field) = explode('.', $name);
3063
3064
                if (isset($result[$key])) {
3065
                    $result[$key][$field] = $closure($result[$key][$field] ?? null, $result[$key]);
3066
                }
3067
            } else {
3068
                $result[$name] = $closure($result[$name] ?? null, $result);
3069
            }
3070
        }
3071
    }
3072
3073
    /**
3074
     * JSON字段数据转换
3075
     * @access protected
3076
     * @param array $result           查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3077
     * @param array $json             JSON字段
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3078
     * @param bool  $assoc            是否转换为数组
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3079
     * @param array $withRelationAttr 关联获取器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3080
     * @return void
3081
     */
3082
    protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void
3083
    {
3084
        foreach ($json as $name) {
3085
            if (!isset($result[$name])) {
3086
                continue;
3087
            }
3088
3089
            $result[$name] = json_decode($result[$name], $assoc);
3090
3091
            if (!isset($withRelationAttr[$name])) {
3092
                continue;
3093
            }
3094
3095
            foreach ($withRelationAttr[$name] as $key => $closure) {
3096
                $data = get_object_vars($result[$name]);
3097
3098
                $result[$name]->$key = $closure($result[$name]->$key ?? null, $data);
3099
            }
3100
        }
3101
    }
3102
3103
    /**
3104
     * 查询数据转换为模型对象
3105
     * @access protected
3106
     * @param array $result           查询数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3107
     * @param array $options          查询参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3108
     * @param bool  $resultSet        是否为数据集查询
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3109
     * @param array $withRelationAttr 关联字段获取器
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3110
     * @return void
3111
     */
3112
    protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void
3113
    {
3114
        // 动态获取器
3115
        if (!empty($options['with_attr']) && empty($withRelationAttr)) {
3116
            foreach ($options['with_attr'] as $name => $val) {
3117
                if (strpos($name, '.')) {
3118
                    list($relation, $field) = explode('.', $name);
3119
3120
                    $withRelationAttr[$relation][$field] = $val;
3121
                    unset($options['with_attr'][$name]);
3122
                }
3123
            }
3124
        }
3125
3126
        // JSON 数据处理
3127
        if (!empty($options['json'])) {
3128
            $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr);
3129
        }
3130
3131
        $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options));
3132
3133
        // 动态获取器
3134
        if (!empty($options['with_attr'])) {
3135
            $result->withAttribute($options['with_attr']);
3136
        }
3137
3138
        // 输出属性控制
3139
        if (!empty($options['visible'])) {
3140
            $result->visible($options['visible']);
3141
        } elseif (!empty($options['hidden'])) {
3142
            $result->hidden($options['hidden']);
3143
        }
3144
3145
        if (!empty($options['append'])) {
3146
            $result->append($options['append']);
3147
        }
3148
3149
        // 关联查询
3150
        if (!empty($options['relation'])) {
3151
            $result->relationQuery($options['relation']);
3152
        }
3153
3154
        // 预载入查询
3155
        if (!$resultSet && !empty($options['with'])) {
3156
            $result->eagerlyResult($result, $options['with'], $withRelationAttr);
3157
        }
3158
3159
        // JOIN预载入查询
3160
        if (!$resultSet && !empty($options['with_join'])) {
3161
            $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true);
3162
        }
3163
3164
        // 关联统计
3165
        if (!empty($options['with_count'])) {
3166
            foreach ($options['with_count'] as $val) {
3167
                $result->relationCount($result, $val[0], $val[1], $val[2]);
3168
            }
3169
        }
3170
    }
3171
3172
    /**
3173
     * 查询失败 抛出异常
3174
     * @access protected
3175
     * @return void
3176
     * @throws ModelNotFoundException
3177
     * @throws DataNotFoundException
3178
     */
3179
    protected function throwNotFound(): void
3180
    {
3181
        if (!empty($this->model)) {
3182
            $class = get_class($this->model);
3183
            throw new ModelNotFoundException('model data Not Found:' . $class, $class, $this->options);
3184
        }
3185
3186
        $table = $this->getTable();
3187
        throw new DataNotFoundException('table data not Found:' . $table, $table, $this->options);
3188
    }
3189
3190
    /**
3191
     * 查找多条记录 如果不存在则抛出异常
3192
     * @access public
3193
     * @param array|string|Query|Closure $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3194
     * @return array|PDOStatement|string|Model
3195
     * @throws DbException
3196
     * @throws ModelNotFoundException
3197
     * @throws DataNotFoundException
3198
     */
3199
    public function selectOrFail($data = null)
3200
    {
3201
        return $this->failException(true)->select($data);
3202
    }
3203
3204
    /**
3205
     * 查找单条记录 如果不存在则抛出异常
3206
     * @access public
3207
     * @param array|string|Query|Closure $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3208
     * @return array|PDOStatement|string|Model
3209
     * @throws DbException
3210
     * @throws ModelNotFoundException
3211
     * @throws DataNotFoundException
3212
     */
3213
    public function findOrFail($data = null)
3214
    {
3215
        return $this->failException(true)->find($data);
3216
    }
3217
3218
    /**
3219
     * 分批数据返回处理
3220
     * @access public
3221
     * @param integer      $count    每次处理的数据数量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3222
     * @param callable     $callback 处理回调方法
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3223
     * @param string|array $column   分批处理的字段名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3224
     * @param string       $order    字段排序
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3225
     * @return bool
3226
     * @throws DbException
3227
     */
3228
    public function chunk(int $count, callable $callback, $column = null, string $order = 'asc'): bool
3229
    {
3230
        $options = $this->getOptions();
3231
        $column  = $column ?: $this->getPk();
3232
3233
        if (isset($options['order'])) {
3234
            unset($options['order']);
3235
        }
3236
3237
        $bind = $this->bind;
3238
3239
        if (is_array($column)) {
3240
            $times = 1;
3241
            $query = $this->options($options)->page($times, $count);
3242
        } else {
3243
            $query = $this->options($options)->limit($count);
3244
3245
            if (strpos($column, '.')) {
3246
                list($alias, $key) = explode('.', $column);
3247
            } else {
3248
                $key = $column;
3249
            }
3250
        }
3251
3252
        $resultSet = $query->order($column, $order)->select();
3253
3254
        while (count($resultSet) > 0) {
3255
            if ($resultSet instanceof Collection) {
3256
                $resultSet = $resultSet->all();
3257
            }
3258
3259
            if (false === call_user_func($callback, $resultSet)) {
3260
                return false;
3261
            }
3262
3263
            if (isset($times)) {
3264
                $times++;
3265
                $query = $this->options($options)->page($times, $count);
3266
            } else {
3267
                $end    = end($resultSet);
3268
                $lastId = is_array($end) ? $end[$key] : $end->getData($key);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $key does not seem to be defined for all execution paths leading up to this point.
Loading history...
3269
3270
                $query = $this->options($options)
3271
                    ->limit($count)
3272
                    ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId);
3273
            }
3274
3275
            $resultSet = $query->bind($bind)->order($column, $order)->select();
3276
        }
3277
3278
        return true;
3279
    }
3280
3281
    /**
3282
     * 获取绑定的参数 并清空
3283
     * @access public
3284
     * @param bool $clear 是否清空绑定数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3285
     * @return array
3286
     */
3287
    public function getBind(bool $clear = true): array
3288
    {
3289
        $bind = $this->bind;
3290
        if ($clear) {
3291
            $this->bind = [];
3292
        }
3293
3294
        return $bind;
3295
    }
3296
3297
    /**
3298
     * 创建子查询SQL
3299
     * @access public
3300
     * @param bool $sub 是否添加括号
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3301
     * @return string
3302
     * @throws DbException
3303
     */
3304
    public function buildSql(bool $sub = true): string
3305
    {
3306
        return $sub ? '( ' . $this->fetchSql()->select() . ' )' : $this->fetchSql()->select();
3307
    }
3308
3309
    /**
3310
     * 视图查询处理
3311
     * @access protected
3312
     * @param array $options 查询参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3313
     * @return void
3314
     */
3315
    protected function parseView(array &$options): void
3316
    {
3317
        foreach (['AND', 'OR'] as $logic) {
3318
            if (isset($options['where'][$logic])) {
3319
                foreach ($options['where'][$logic] as $key => $val) {
3320
                    if (array_key_exists($key, $options['map'])) {
3321
                        array_shift($val);
3322
                        array_unshift($val, $options['map'][$key]);
3323
                        $options['where'][$logic][$options['map'][$key]] = $val;
3324
                        unset($options['where'][$logic][$key]);
3325
                    }
3326
                }
3327
            }
3328
        }
3329
3330
        if (isset($options['order'])) {
3331
            // 视图查询排序处理
3332
            foreach ($options['order'] as $key => $val) {
3333
                if (is_numeric($key) && is_string($val)) {
3334
                    if (strpos($val, ' ')) {
3335
                        list($field, $sort) = explode(' ', $val);
3336
                        if (array_key_exists($field, $options['map'])) {
3337
                            $options['order'][$options['map'][$field]] = $sort;
3338
                            unset($options['order'][$key]);
3339
                        }
3340
                    } elseif (array_key_exists($val, $options['map'])) {
3341
                        $options['order'][$options['map'][$val]] = 'asc';
3342
                        unset($options['order'][$key]);
3343
                    }
3344
                } elseif (array_key_exists($key, $options['map'])) {
3345
                    $options['order'][$options['map'][$key]] = $val;
3346
                    unset($options['order'][$key]);
3347
                }
3348
            }
3349
        }
3350
    }
3351
3352
    /**
3353
     * 分析数据是否存在更新条件
3354
     * @access public
3355
     * @param array $data 数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3356
     * @return bool
3357
     * @throws Exception
3358
     */
3359
    public function parseUpdateData(&$data): bool
3360
    {
3361
        $pk       = $this->getPk();
3362
        $isUpdate = false;
3363
        // 如果存在主键数据 则自动作为更新条件
3364
        if (is_string($pk) && isset($data[$pk])) {
3365
            $this->where($pk, '=', $data[$pk]);
3366
            $this->options['key'] = $data[$pk];
3367
            unset($data[$pk]);
3368
            $isUpdate = true;
3369
        } elseif (is_array($pk)) {
3370
            // 增加复合主键支持
3371
            foreach ($pk as $field) {
3372
                if (isset($data[$field])) {
3373
                    $this->where($field, '=', $data[$field]);
3374
                    $isUpdate = true;
3375
                } else {
3376
                    // 如果缺少复合主键数据则不执行
3377
                    throw new Exception('miss complex primary data');
3378
                }
3379
                unset($data[$field]);
3380
            }
3381
        }
3382
3383
        return $isUpdate;
3384
    }
3385
3386
    /**
3387
     * 把主键值转换为查询条件 支持复合主键
3388
     * @access public
3389
     * @param array|string $data 主键数据
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
3390
     * @return void
3391
     * @throws Exception
3392
     */
3393
    public function parsePkWhere($data): void
3394
    {
3395
        $pk = $this->getPk();
3396
3397
        if (is_string($pk)) {
3398
            // 获取数据表
3399
            if (empty($this->options['table'])) {
3400
                $this->options['table'] = $this->getTable();
3401
            }
3402
3403
            $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
3404
3405
            if (!empty($this->options['alias'][$table])) {
3406
                $alias = $this->options['alias'][$table];
3407
            }
3408
3409
            $key = isset($alias) ? $alias . '.' . $pk : $pk;
3410
            // 根据主键查询
3411
            if (is_array($data)) {
3412
                $this->where($key, 'in', $data);
3413
            } else {
3414
                $this->where($key, '=', $data);
3415
                $this->options['key'] = $data;
3416
            }
3417
        }
3418
    }
3419
3420
    /**
3421
     * 分析表达式(可用于查询或者写入操作)
3422
     * @access public
3423
     * @return array
3424
     */
3425
    public function parseOptions(): array
3426
    {
3427
        $options = $this->getOptions();
3428
3429
        // 获取数据表
3430
        if (empty($options['table'])) {
3431
            $options['table'] = $this->getTable();
3432
        }
3433
3434
        if (!isset($options['where'])) {
3435
            $options['where'] = [];
3436
        } elseif (isset($options['view'])) {
3437
            // 视图查询条件处理
3438
            $this->parseView($options);
3439
        }
3440
3441
        if (!isset($options['field'])) {
3442
            $options['field'] = '*';
3443
        }
3444
3445
        foreach (['data', 'order', 'join', 'union'] as $name) {
3446
            if (!isset($options[$name])) {
3447
                $options[$name] = [];
3448
            }
3449
        }
3450
3451
        if (!isset($options['strict'])) {
3452
            $options['strict'] = $this->connection->getConfig('fields_strict');
3453
        }
3454
3455
        foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) {
3456
            if (!isset($options[$name])) {
3457
                $options[$name] = false;
3458
            }
3459
        }
3460
3461
        foreach (['group', 'having', 'limit', 'force', 'comment', 'partition', 'duplicate', 'extra'] as $name) {
3462
            if (!isset($options[$name])) {
3463
                $options[$name] = '';
3464
            }
3465
        }
3466
3467
        if (isset($options['page'])) {
3468
            // 根据页数计算limit
3469
            list($page, $listRows) = $options['page'];
3470
            $page                  = $page > 0 ? $page : 1;
3471
            $listRows              = $listRows ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
3472
            $offset                = $listRows * ($page - 1);
3473
            $options['limit']      = $offset . ',' . $listRows;
3474
        }
3475
3476
        $this->options = $options;
3477
3478
        return $options;
3479
    }
3480
3481
}
3482