Passed
Pull Request — 5.1 (#1748)
by guanguans
09:27
created

Model::__set()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
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~2018 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
12
namespace think;
13
14
use InvalidArgumentException;
15
use think\db\Query;
16
17
/**
18
 * Class Model
19
 * @package think
0 ignored issues
show
Coding Style introduced by
Package name "think" is not valid; consider "Think" instead
Loading history...
20
 * @mixin Query
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 3 spaces but found 1
Loading history...
21
 * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
22
 * @method Query whereRaw(string $where, array $bind = []) static 表达式查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
23
 * @method Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
24
 * @method Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
25
 * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
26
 * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
27
 * @method Query with(mixed $with) static 关联预载入
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
28
 * @method Query count(string $field) static Count统计查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
29
 * @method Query min(string $field) static Min统计查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
30
 * @method Query max(string $field) static Max统计查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
31
 * @method Query sum(string $field) static SUM统计查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
32
 * @method Query avg(string $field) static Avg统计查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
33
 * @method Query field(mixed $field, boolean $except = false) static 指定查询字段
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
34
 * @method Query fieldRaw(string $field, array $bind = []) static 指定查询字段
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
35
 * @method Query union(mixed $union, boolean $all = false) static UNION查询
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
36
 * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
37
 * @method Query order(mixed $field, string $order = null) static 查询ORDER
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
38
 * @method Query orderRaw(string $field, array $bind = []) static 查询ORDER
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
39
 * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
40
 * @method mixed value(string $field) static 获取某个字段的值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
41
 * @method array column(string $field, string $key = '') static 获取某个列的值
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
42
 * @method mixed find(mixed $data = null) static 查询单个记录
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
43
 * @method mixed select(mixed $data = null) static 查询多个记录
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
44
 * @method mixed get(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 支持关联预载入
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
45
 * @method mixed getOrFail(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 不存在则抛出异常
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
46
 * @method mixed findOrEmpty(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录  不存在则返回空模型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
47
 * @method mixed all(mixed $data = null,mixed $with =[],bool $cache= false) static 查询多个记录 支持关联预载入
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
48
 * @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
49
 */
4 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
50
abstract class Model implements \JsonSerializable, \ArrayAccess
51
{
52
    use model\concern\Attribute;
53
    use model\concern\RelationShip;
54
    use model\concern\ModelEvent;
55
    use model\concern\TimeStamp;
56
    use model\concern\Conversion;
57
58
    /**
59
     * 是否存在数据
60
     * @var bool
61
     */
62
    private $exists = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "exists" must be prefixed with an underscore
Loading history...
63
64
    /**
65
     * 是否Replace
66
     * @var bool
67
     */
68
    private $replace = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "replace" must be prefixed with an underscore
Loading history...
69
70
    /**
71
     * 是否强制更新所有数据
72
     * @var bool
73
     */
74
    private $force = false;
0 ignored issues
show
Coding Style introduced by
Private member variable "force" must be prefixed with an underscore
Loading history...
75
76
    /**
77
     * 更新条件
78
     * @var array
79
     */
80
    private $updateWhere;
0 ignored issues
show
Coding Style introduced by
Private member variable "updateWhere" must be prefixed with an underscore
Loading history...
81
82
    /**
83
     * 数据库配置信息
84
     * @var array|string
85
     */
86
    protected $connection = [];
87
88
    /**
89
     * 数据库查询对象类名
90
     * @var string
91
     */
92
    protected $query;
93
94
    /**
95
     * 模型名称
96
     * @var string
97
     */
98
    protected $name;
99
100
    /**
101
     * 数据表名称
102
     * @var string
103
     */
104
    protected $table;
105
106
    /**
107
     * 写入自动完成定义
108
     * @var array
109
     */
110
    protected $auto = [];
111
112
    /**
113
     * 新增自动完成定义
114
     * @var array
115
     */
116
    protected $insert = [];
117
118
    /**
119
     * 更新自动完成定义
120
     * @var array
121
     */
122
    protected $update = [];
123
124
    /**
125
     * 初始化过的模型.
126
     * @var array
127
     */
128
    protected static $initialized = [];
129
130
    /**
131
     * 是否从主库读取(主从分布式有效)
132
     * @var array
133
     */
134
    protected static $readMaster;
135
136
    /**
137
     * 查询对象实例
138
     * @var Query
139
     */
140
    protected $queryInstance;
141
142
    /**
143
     * 错误信息
144
     * @var mixed
145
     */
146
    protected $error;
147
148
    /**
149
     * 软删除字段默认值
150
     * @var mixed
151
     */
152
    protected $defaultSoftDelete;
153
154
    /**
155
     * 全局查询范围
156
     * @var array
157
     */
158
    protected $globalScope = [];
159
160
    /**
161
     * 架构函数
162
     * @access public
163
     * @param  array|object $data 数据
164
     */
165
    public function __construct($data = [])
166
    {
167
        if (is_object($data)) {
168
            $this->data = get_object_vars($data);
169
        } else {
170
            $this->data = $data;
171
        }
172
173
        if ($this->disuse) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->disuse of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
174
            // 废弃字段
175
            foreach ((array) $this->disuse as $key) {
176
                if (array_key_exists($key, $this->data)) {
177
                    unset($this->data[$key]);
178
                }
179
            }
180
        }
181
182
        // 记录原始数据
183
        $this->origin = $this->data;
184
185
        $config = Db::getConfig();
186
187
        if (empty($this->name)) {
188
            // 当前模型名
189
            $name       = str_replace('\\', '/', static::class);
190
            $this->name = basename($name);
191
            if (Container::get('config')->get('class_suffix')) {
192
                $suffix     = basename(dirname($name));
193
                $this->name = substr($this->name, 0, -strlen($suffix));
194
            }
195
        }
196
197
        if (is_null($this->autoWriteTimestamp)) {
0 ignored issues
show
introduced by
The condition is_null($this->autoWriteTimestamp) is always false.
Loading history...
198
            // 自动写入时间戳
199
            $this->autoWriteTimestamp = $config['auto_timestamp'];
200
        }
201
202
        if (is_null($this->dateFormat)) {
0 ignored issues
show
introduced by
The condition is_null($this->dateFormat) is always false.
Loading history...
203
            // 设置时间戳格式
204
            $this->dateFormat = $config['datetime_format'];
205
        }
206
207
        if (is_null($this->resultSetType)) {
0 ignored issues
show
introduced by
The condition is_null($this->resultSetType) is always false.
Loading history...
208
            $this->resultSetType = $config['resultset_type'];
209
        }
210
211
        if (!empty($this->connection) && is_array($this->connection)) {
212
            // 设置模型的数据库连接
213
            $this->connection = array_merge($config, $this->connection);
214
        }
215
216
        if ($this->observerClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->observerClass of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
217
            // 注册模型观察者
218
            static::observe($this->observerClass);
0 ignored issues
show
Bug introduced by
$this->observerClass of type array is incompatible with the type object|string expected by parameter $class of think\Model::observe(). ( Ignorable by Annotation )

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

218
            static::observe(/** @scrutinizer ignore-type */ $this->observerClass);
Loading history...
219
        }
220
221
        // 执行初始化操作
222
        $this->initialize();
223
    }
224
225
    /**
226
     * 获取当前模型名称
227
     * @access public
228
     * @return string
229
     */
230
    public function getName()
231
    {
232
        return $this->name;
233
    }
234
235
    /**
236
     * 是否从主库读取数据(主从分布有效)
237
     * @access public
238
     * @param  bool     $all 是否所有模型有效
239
     * @return $this
240
     */
241
    public function readMaster($all = false)
242
    {
243
        $model = $all ? '*' : static::class;
244
245
        static::$readMaster[$model] = true;
246
247
        return $this;
248
    }
249
250
    /**
251
     * 创建新的模型实例
252
     * @access public
253
     * @param  array|object $data 数据
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
254
     * @param  bool         $isUpdate 是否为更新
255
     * @param  mixed        $where 更新条件
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
256
     * @return Model
257
     */
258
    public function newInstance($data = [], $isUpdate = false, $where = null)
259
    {
260
        return (new static($data))->isUpdate($isUpdate, $where);
261
    }
262
263
    /**
264
     * 创建模型的查询对象
265
     * @access protected
266
     * @return Query
267
     */
268
    protected function buildQuery()
269
    {
270
        // 设置当前模型 确保查询返回模型对象
271
        $query = Db::connect($this->connection, false, $this->query);
272
        $query->model($this)
273
            ->name($this->name)
274
            ->json($this->json, $this->jsonAssoc)
275
            ->setJsonFieldType($this->jsonType);
276
277
        if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) {
278
            $query->master(true);
279
        }
280
281
        // 设置当前数据表和模型名
282
        if (!empty($this->table)) {
283
            $query->table($this->table);
284
        }
285
286
        if (!empty($this->pk)) {
287
            $query->pk($this->pk);
288
        }
289
290
        return $query;
291
    }
292
293
    /**
294
     * 获取当前模型的数据库查询对象
295
     * @access public
296
     * @param  Query $query 查询对象实例
297
     * @return $this
298
     */
299
    public function setQuery($query)
300
    {
301
        $this->queryInstance = $query;
302
        return $this;
303
    }
304
305
    /**
306
     * 获取当前模型的数据库查询对象
307
     * @access public
308
     * @param  bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称)
309
     * @return Query
310
     */
311
    public function db($useBaseQuery = true)
312
    {
313
        if ($this->queryInstance) {
314
            return $this->queryInstance;
315
        }
316
317
        $query = $this->buildQuery();
318
319
        // 软删除
320
        if (property_exists($this, 'withTrashed') && !$this->withTrashed) {
0 ignored issues
show
Bug Best Practice introduced by
The property withTrashed does not exist on think\Model. Since you implemented __get, consider adding a @property annotation.
Loading history...
321
            $this->withNoTrashed($query);
0 ignored issues
show
Bug introduced by
The method withNoTrashed() does not exist on think\Model. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

321
            $this->/** @scrutinizer ignore-call */ 
322
                   withNoTrashed($query);
Loading history...
322
        }
323
324
        // 全局作用域
325
        if (true === $useBaseQuery && method_exists($this, 'base')) {
326
            call_user_func_array([$this, 'base'], [ & $query]);
327
        }
328
329
        $globalScope = is_array($useBaseQuery) && $useBaseQuery ? $useBaseQuery : $this->globalScope;
0 ignored issues
show
Bug Best Practice introduced by
The expression $useBaseQuery of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
330
331
        if ($globalScope && false !== $useBaseQuery) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $globalScope of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
332
            $query->scope($globalScope);
333
        }
334
335
        // 返回当前模型的数据库查询对象
336
        return $query;
337
    }
338
339
    /**
340
     *  初始化模型
341
     * @access protected
342
     * @return void
343
     */
344
    protected function initialize()
345
    {
346
        if (!isset(static::$initialized[static::class])) {
347
            static::$initialized[static::class] = true;
348
            static::init();
349
        }
350
    }
351
352
    /**
353
     * 初始化处理
354
     * @access protected
355
     * @return void
356
     */
357
    protected static function init()
358
    {}
0 ignored issues
show
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
359
360
    /**
361
     * 数据自动完成
362
     * @access protected
363
     * @param  array $auto 要自动更新的字段列表
364
     * @return void
365
     */
366
    protected function autoCompleteData($auto = [])
367
    {
368
        foreach ($auto as $field => $value) {
369
            if (is_integer($field)) {
370
                $field = $value;
371
                $value = null;
372
            }
373
374
            if (!isset($this->data[$field])) {
375
                $default = null;
376
            } else {
377
                $default = $this->data[$field];
378
            }
379
380
            $this->setAttr($field, !is_null($value) ? $value : $default);
381
        }
382
    }
383
384
    /**
385
     * 更新是否强制写入数据 而不做比较
386
     * @access public
387
     * @param  bool $force
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
388
     * @return $this
389
     */
390
    public function force($force = true)
391
    {
392
        $this->force = $force;
393
        return $this;
394
    }
395
396
    /**
397
     * 判断force
398
     * @access public
399
     * @return bool
400
     */
401
    public function isForce()
402
    {
403
        return $this->force;
404
    }
405
406
    /**
407
     * 新增数据是否使用Replace
408
     * @access public
409
     * @param  bool $replace
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
410
     * @return $this
411
     */
412
    public function replace($replace = true)
413
    {
414
        $this->replace = $replace;
415
        return $this;
416
    }
417
418
    /**
419
     * 设置数据是否存在
420
     * @access public
421
     * @param  bool $exists
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
422
     * @return $this
423
     */
424
    public function exists($exists)
425
    {
426
        $this->exists = $exists;
427
        return $this;
428
    }
429
430
    /**
431
     * 判断数据是否存在数据库
432
     * @access public
433
     * @return bool
434
     */
435
    public function isExists()
436
    {
437
        return $this->exists;
438
    }
439
440
    /**
441
     * 判断模型是否为空
442
     * @access public
443
     * @return bool
444
     */
445
    public function isEmpty()
446
    {
447
        return empty($this->data);
448
    }
449
450
    /**
451
     * 保存当前数据对象
452
     * @access public
453
     * @param  array  $data     数据
454
     * @param  array  $where    更新条件
455
     * @param  string $sequence 自增序列名
456
     * @return bool
457
     */
458
    public function save($data = [], $where = [], $sequence = null)
459
    {
460
        if (is_string($data)) {
0 ignored issues
show
introduced by
The condition is_string($data) is always false.
Loading history...
461
            $sequence = $data;
462
            $data     = [];
463
        }
464
465
        if (!$this->checkBeforeSave($data, $where)) {
466
            return false;
467
        }
468
469
        $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence);
470
471
        if (false === $result) {
472
            return false;
473
        }
474
475
        // 写入回调
476
        $this->trigger('after_write');
477
478
        // 重新记录原始数据
479
        $this->origin = $this->data;
480
        $this->set    = [];
481
482
        return true;
483
    }
484
485
    /**
486
     * 写入之前检查数据
487
     * @access protected
488
     * @param  array   $data  数据
489
     * @param  array   $where 保存条件
490
     * @return bool
491
     */
492
    protected function checkBeforeSave($data, $where)
493
    {
494
        if (!empty($data)) {
495
            // 数据对象赋值
496
            foreach ($data as $key => $value) {
497
                $this->setAttr($key, $value, $data);
498
            }
499
500
            if (!empty($where)) {
501
                $this->exists      = true;
502
                $this->updateWhere = $where;
503
            }
504
        }
505
506
        // 数据自动完成
507
        $this->autoCompleteData($this->auto);
508
509
        // 事件回调
510
        if (false === $this->trigger('before_write')) {
511
            return false;
512
        }
513
514
        return true;
515
    }
516
517
    /**
518
     * 检查数据是否允许写入
519
     * @access protected
520
     * @param  array   $append 自动完成的字段列表
521
     * @return array
522
     */
523
    protected function checkAllowFields(array $append = [])
524
    {
525
        // 检测字段
526
        if (empty($this->field) || true === $this->field) {
527
            $query = $this->db(false);
528
            $table = $this->table ?: $query->getTable();
529
530
            $this->field = $query->getConnection()->getTableFields($table);
531
532
            $field = $this->field;
533
        } else {
534
            $field = array_merge($this->field, $append);
535
536
            if ($this->autoWriteTimestamp) {
537
                array_push($field, $this->createTime, $this->updateTime);
538
            }
539
        }
540
541
        if ($this->disuse) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->disuse of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
542
            // 废弃字段
543
            $field = array_diff($field, (array) $this->disuse);
544
        }
545
546
        return $field;
547
    }
548
549
    /**
550
     * 更新写入数据
551
     * @access protected
552
     * @param  mixed   $where 更新条件
553
     * @return bool
554
     */
555
    protected function updateData($where)
556
    {
557
        // 自动更新
558
        $this->autoCompleteData($this->update);
559
560
        // 事件回调
561
        if (false === $this->trigger('before_update')) {
562
            return false;
563
        }
564
565
        // 获取有更新的数据
566
        $data = $this->getChangedData();
567
568
        if (empty($data)) {
569
            // 关联更新
570
            if (!empty($this->relationWrite)) {
571
                $this->autoRelationUpdate();
572
            }
573
574
            return false;
575
        } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
576
            // 自动写入更新时间
577
            $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
0 ignored issues
show
Bug introduced by
It seems like $this->updateTime can also be of type true; however, parameter $name of think\Model::autoWriteTimestamp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

577
            $data[$this->updateTime] = $this->autoWriteTimestamp(/** @scrutinizer ignore-type */ $this->updateTime);
Loading history...
578
579
            $this->data[$this->updateTime] = $data[$this->updateTime];
580
        }
581
582
        if (empty($where) && !empty($this->updateWhere)) {
583
            $where = $this->updateWhere;
584
        }
585
586
        // 检查允许字段
587
        $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update));
588
589
        // 保留主键数据
590
        foreach ($this->data as $key => $val) {
591
            if ($this->isPk($key)) {
592
                $data[$key] = $val;
593
            }
594
        }
595
596
        $pk    = $this->getPk();
597
        $array = [];
598
599
        foreach ((array) $pk as $key) {
600
            if (isset($data[$key])) {
601
                $array[] = [$key, '=', $data[$key]];
602
                unset($data[$key]);
603
            }
604
        }
605
606
        if (!empty($array)) {
607
            $where = $array;
608
        }
609
610
        foreach ((array) $this->relationWrite as $name => $val) {
611
            if (is_array($val)) {
612
                foreach ($val as $key) {
613
                    if (isset($data[$key])) {
614
                        unset($data[$key]);
615
                    }
616
                }
617
            }
618
        }
619
620
        // 模型更新
621
        $db = $this->db(false);
622
        $db->startTrans();
623
624
        try {
625
            $db->where($where)
626
                ->strict(false)
627
                ->field($allowFields)
628
                ->update($data);
629
630
            // 关联更新
631
            if (!empty($this->relationWrite)) {
632
                $this->autoRelationUpdate();
633
            }
634
635
            $db->commit();
636
637
            // 更新回调
638
            $this->trigger('after_update');
639
640
            return true;
641
        } catch (\Exception $e) {
642
            $db->rollback();
643
            throw $e;
644
        }
645
    }
646
647
    /**
648
     * 新增写入数据
649
     * @access protected
650
     * @param  string   $sequence 自增序列名
651
     * @return bool
652
     */
653
    protected function insertData($sequence)
654
    {
655
        // 自动写入
656
        $this->autoCompleteData($this->insert);
657
658
        // 时间戳自动写入
659
        $this->checkTimeStampWrite();
660
661
        if (false === $this->trigger('before_insert')) {
662
            return false;
663
        }
664
665
        // 检查允许字段
666
        $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert));
667
668
        $db = $this->db(false);
669
        $db->startTrans();
670
671
        try {
672
            $result = $db->strict(false)
673
                ->field($allowFields)
674
                ->insert($this->data, $this->replace, false, $sequence);
675
676
            // 获取自动增长主键
677
            if ($result && $insertId = $db->getLastInsID($sequence)) {
678
                $pk = $this->getPk();
679
680
                foreach ((array) $pk as $key) {
681
                    if (!isset($this->data[$key]) || '' == $this->data[$key]) {
682
                        $this->data[$key] = $insertId;
683
                    }
684
                }
685
            }
686
687
            // 关联写入
688
            if (!empty($this->relationWrite)) {
689
                $this->autoRelationInsert();
690
            }
691
692
            $db->commit();
693
694
            // 标记为更新
695
            $this->exists = true;
696
697
            // 新增回调
698
            $this->trigger('after_insert');
699
700
            return true;
701
        } catch (\Exception $e) {
702
            $db->rollback();
703
            throw $e;
704
        }
705
    }
706
707
    /**
708
     * 字段值(延迟)增长
709
     * @access public
710
     * @param  string  $field    字段名
711
     * @param  integer $step     增长值
712
     * @param  integer $lazyTime 延时时间(s)
713
     * @return bool
714
     * @throws Exception
715
     */
716
    public function setInc($field, $step = 1, $lazyTime = 0)
717
    {
718
        // 读取更新条件
719
        $where = $this->getWhere();
720
721
        // 事件回调
722
        if (false === $this->trigger('before_update')) {
723
            return false;
724
        }
725
726
        $result = $this->db(false)
727
            ->where($where)
728
            ->setInc($field, $step, $lazyTime);
729
730
        if (true !== $result) {
731
            $this->data[$field] += $step;
732
        }
733
734
        // 更新回调
735
        $this->trigger('after_update');
736
737
        return true;
738
    }
739
740
    /**
741
     * 字段值(延迟)减少
742
     * @access public
743
     * @param  string  $field    字段名
744
     * @param  integer $step     减少值
745
     * @param  integer $lazyTime 延时时间(s)
746
     * @return bool
747
     * @throws Exception
748
     */
749
    public function setDec($field, $step = 1, $lazyTime = 0)
750
    {
751
        // 读取更新条件
752
        $where = $this->getWhere();
753
754
        // 事件回调
755
        if (false === $this->trigger('before_update')) {
756
            return false;
757
        }
758
759
        $result = $this->db(false)
760
            ->where($where)
761
            ->setDec($field, $step, $lazyTime);
762
763
        if (true !== $result) {
764
            $this->data[$field] -= $step;
765
        }
766
767
        // 更新回调
768
        $this->trigger('after_update');
769
770
        return true;
771
    }
772
773
    /**
774
     * 获取当前的更新条件
775
     * @access protected
776
     * @return mixed
777
     */
778
    protected function getWhere()
779
    {
780
        // 删除条件
781
        $pk = $this->getPk();
782
783
        if (is_string($pk) && isset($this->data[$pk])) {
784
            $where[] = [$pk, '=', $this->data[$pk]];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$where was never initialized. Although not strictly required by PHP, it is generally a good practice to add $where = array(); before regardless.
Loading history...
785
        } elseif (!empty($this->updateWhere)) {
786
            $where = $this->updateWhere;
787
        } else {
788
            $where = null;
789
        }
790
791
        return $where;
792
    }
793
794
    /**
795
     * 保存多个数据到当前数据对象
796
     * @access public
797
     * @param  array   $dataSet 数据
798
     * @param  boolean $replace 是否自动识别更新和写入
799
     * @return Collection
800
     * @throws \Exception
801
     */
802
    public function saveAll($dataSet, $replace = true)
803
    {
804
        $db = $this->db(false);
805
        $db->startTrans();
806
807
        try {
808
            $pk = $this->getPk();
809
810
            if (is_string($pk) && $replace) {
811
                $auto = true;
812
            }
813
814
            $result = [];
815
816
            foreach ($dataSet as $key => $data) {
817
                if ($this->exists || (!empty($auto) && isset($data[$pk]))) {
818
                    $result[$key] = self::update($data, [], $this->field);
819
                } else {
820
                    $result[$key] = self::create($data, $this->field, $this->replace);
821
                }
822
            }
823
824
            $db->commit();
825
826
            return $this->toCollection($result);
827
        } catch (\Exception $e) {
828
            $db->rollback();
829
            throw $e;
830
        }
831
    }
832
833
    /**
834
     * 是否为更新数据
835
     * @access public
836
     * @param  mixed  $update
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
837
     * @param  mixed  $where
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
838
     * @return $this
839
     */
840
    public function isUpdate($update = true, $where = null)
841
    {
842
        if (is_bool($update)) {
843
            $this->exists = $update;
844
845
            if (!empty($where)) {
846
                $this->updateWhere = $where;
847
            }
848
        } else {
849
            $this->exists      = true;
850
            $this->updateWhere = $update;
851
        }
852
853
        return $this;
854
    }
855
856
    /**
857
     * 删除当前的记录
858
     * @access public
859
     * @return bool
860
     */
861
    public function delete()
862
    {
863
        if (!$this->exists || false === $this->trigger('before_delete')) {
864
            return false;
865
        }
866
867
        // 读取更新条件
868
        $where = $this->getWhere();
869
870
        $db = $this->db(false);
871
        $db->startTrans();
872
873
        try {
874
            // 删除当前模型数据
875
            $db->where($where)->delete();
876
877
            // 关联删除
878
            if (!empty($this->relationWrite)) {
879
                $this->autoRelationDelete();
880
            }
881
882
            $db->commit();
883
884
            $this->trigger('after_delete');
885
886
            $this->exists = false;
887
888
            return true;
889
        } catch (\Exception $e) {
890
            $db->rollback();
891
            throw $e;
892
        }
893
    }
894
895
    /**
896
     * 设置自动完成的字段( 规则通过修改器定义)
897
     * @access public
898
     * @param  array $fields 需要自动完成的字段
899
     * @return $this
900
     */
901
    public function auto($fields)
902
    {
903
        $this->auto = $fields;
904
905
        return $this;
906
    }
907
908
    /**
909
     * 写入数据
910
     * @access public
911
     * @param  array      $data  数据数组
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 2 found
Loading history...
912
     * @param  array|true $field 允许字段
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
913
     * @param  bool       $replace 使用Replace
914
     * @return static
915
     */
916
    public static function create($data = [], $field = null, $replace = false)
917
    {
918
        $model = new static();
919
920
        if (!empty($field)) {
921
            $model->allowField($field);
922
        }
923
924
        $model->isUpdate(false)->replace($replace)->save($data, []);
925
926
        return $model;
927
    }
928
929
    /**
930
     * 更新数据
931
     * @access public
932
     * @param  array      $data  数据数组
933
     * @param  array      $where 更新条件
934
     * @param  array|true $field 允许字段
935
     * @return static
936
     */
937
    public static function update($data = [], $where = [], $field = null)
938
    {
939
        $model = new static();
940
941
        if (!empty($field)) {
942
            $model->allowField($field);
943
        }
944
945
        $model->isUpdate(true)->save($data, $where);
946
947
        return $model;
948
    }
949
950
    /**
951
     * 删除记录
952
     * @access public
953
     * @param  mixed $data 主键列表 支持闭包查询条件
954
     * @return bool
955
     */
956
    public static function destroy($data)
957
    {
958
        if (empty($data) && 0 !== $data) {
959
            return false;
960
        }
961
962
        $model = new static();
963
964
        $query = $model->db();
965
966
        if (is_array($data) && key($data) !== 0) {
967
            $query->where($data);
968
            $data = null;
969
        } elseif ($data instanceof \Closure) {
970
            $data($query);
971
            $data = null;
972
        }
973
974
        $resultSet = $query->select($data);
975
976
        if ($resultSet) {
977
            foreach ($resultSet as $data) {
0 ignored issues
show
introduced by
$data is overwriting one of the parameters of this function.
Loading history...
978
                $data->delete();
979
            }
980
        }
981
982
        return true;
983
    }
984
985
    /**
986
     * 获取错误信息
987
     * @access public
988
     * @return mixed
989
     */
990
    public function getError()
991
    {
992
        return $this->error;
993
    }
994
995
    /**
996
     * 解序列化后处理
997
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
998
    public function __wakeup()
999
    {
1000
        $this->initialize();
1001
    }
1002
1003
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1004
    {
1005
        return [
1006
            'data'     => $this->data,
1007
            'relation' => $this->relation,
1008
        ];
1009
    }
1010
1011
    /**
1012
     * 修改器 设置数据对象的值
1013
     * @access public
1014
     * @param  string $name  名称
1015
     * @param  mixed  $value 值
1016
     * @return void
1017
     */
1018
    public function __set($name, $value)
1019
    {
1020
        $this->setAttr($name, $value);
1021
    }
1022
1023
    /**
1024
     * 获取器 获取数据对象的值
1025
     * @access public
1026
     * @param  string $name 名称
1027
     * @return mixed
1028
     */
1029
    public function __get($name)
1030
    {
1031
        return $this->getAttr($name);
1032
    }
1033
1034
    /**
1035
     * 检测数据对象的值
1036
     * @access public
1037
     * @param  string $name 名称
1038
     * @return boolean
1039
     */
1040
    public function __isset($name)
1041
    {
1042
        try {
1043
            return !is_null($this->getAttr($name));
1044
        } catch (InvalidArgumentException $e) {
1045
            return false;
1046
        }
1047
    }
1048
1049
    /**
1050
     * 销毁数据对象的值
1051
     * @access public
1052
     * @param  string $name 名称
1053
     * @return void
1054
     */
1055
    public function __unset($name)
1056
    {
1057
        unset($this->data[$name], $this->relation[$name]);
1058
    }
1059
1060
    // ArrayAccess
1061
    public function offsetSet($name, $value)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
1062
    {
1063
        $this->setAttr($name, $value);
1064
    }
1065
1066
    public function offsetExists($name)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1067
    {
1068
        return $this->__isset($name);
1069
    }
1070
1071
    public function offsetUnset($name)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1072
    {
1073
        $this->__unset($name);
1074
    }
1075
1076
    public function offsetGet($name)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1077
    {
1078
        return $this->getAttr($name);
1079
    }
1080
1081
    /**
1082
     * 设置是否使用全局查询范围
1083
     * @access public
1084
     * @param  bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称)
1085
     * @return Query
1086
     */
1087
    public static function useGlobalScope($use)
1088
    {
1089
        $model = new static();
1090
1091
        return $model->db($use);
1092
    }
1093
1094
    public function __call($method, $args)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1095
    {
1096
        if ('withattr' == strtolower($method)) {
1097
            return call_user_func_array([$this, 'withAttribute'], $args);
1098
        }
1099
1100
        return call_user_func_array([$this->db(), $method], $args);
1101
    }
1102
1103
    public static function __callStatic($method, $args)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1104
    {
1105
        $model = new static();
1106
1107
        return call_user_func_array([$model->db(), $method], $args);
1108
    }
1109
}
1110