// +---------------------------------------------------------------------- declare (strict_types = 1); namespace think\model\concern; use Closure; use think\db\BaseQuery as Query; use think\Model; /** * 数据软删除 * * @mixin Model * * @method $this withTrashed() * @method $this onlyTrashed() */ trait SoftDelete { /** * 获取查询对象 * * @param array|null $scope 设置不使用的全局查询范围 * @return Query */ public function db(array | null $scope = []): Query { $query = parent::db($scope); $this->withNoTrashed($query); return $query; } /** * 判断当前实例是否被软删除. * * @return bool */ public function trashed(): bool { $field = $this->getDeleteTimeField(); if ($field && !empty($this->getOrigin($field))) { return true; } return false; } public function scopeWithTrashed(Query $query): void { $query->removeOption('soft_delete'); } public function scopeOnlyTrashed(Query $query): void { $field = $this->getDeleteTimeField(true); if ($field) { $query->useSoftDelete($field, $this->getWithTrashedExp()); } } /** * 获取软删除数据的查询条件. * * @return array */ protected function getWithTrashedExp(): array { return is_null($this->getOption('defaultSoftDelete')) ? ['notnull', ''] : ['<>', $this->getOption('defaultSoftDelete')]; } /** * 删除当前的记录. * * @return bool */ public function delete(): bool { if ($this->isEmpty() || false === $this->trigger('BeforeDelete')) { return false; } $name = $this->getDeleteTimeField(); $force = $this->isForce(); if ($name && !$force) { // 软删除 $this->exists()->withEvent(false)->save([$name => $this->getDateTime($name)]); $this->withEvent(true); $this->trigger('AfterDelete'); $this->exists(false); $this->clear(); return true; } return parent::delete(); } /** * 删除记录. * * @param mixed $data 主键列表 支持闭包查询条件 * @param bool $force 是否强制删除 * * @return bool */ public static function destroy($data, bool $force = false): bool { // 传入空值(包括空字符串和空数组)的时候不会做任何的数据删除操作,但传入0则是有效的 if (empty($data) && 0 !== $data) { return false; } $query = (new static())->db(); if ($force) { $query->removeOption('soft_delete'); } if (is_array($data) && key($data) !== 0) { $query->where($data); $data = []; } elseif ($data instanceof Closure) { call_user_func_array($data, [ &$query]); $data = []; } $resultSet = $query->select((array) $data); foreach ($resultSet as $result) { /** @var Model $result */ $result->force($force)->delete(); } return true; } /** * 恢复被软删除的记录. * * @param array $where 更新条件 * * @return bool */ public function restore(array $where = []): bool { $name = $this->getDeleteTimeField(); if (!$name || false === $this->trigger('BeforeRestore')) { return false; } $db = $this->getDbWhere($where); // 恢复删除 $db->useSoftDelete($name, $this->getWithTrashedExp()) ->update([$name => $this->getOption('defaultSoftDelete')]); $this->trigger('AfterRestore'); return true; } /** * 获取软删除字段. * * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 * * @return string|false */ public function getDeleteTimeField(bool $read = false): bool | string { $field = $this->getOption('deleteTime', 'delete_time'); if (false === $field) { return false; } if (!str_contains($field, '.')) { $field = '__TABLE__.' . $field; } if (!$read && str_contains($field, '.')) { $array = explode('.', $field); $field = array_pop($array); } return $field; } /** * 查询的时候默认排除软删除数据. * * @param Query $query * * @return void */ protected function withNoTrashed(Query $query): void { $field = $this->getDeleteTimeField(true); if ($field) { $condition = is_null($this->getOption('defaultSoftDelete')) ? ['null', ''] : ['=', $this->getOption('defaultSoftDelete')]; $query->useSoftDelete($field, $condition); } } }