thinkphp 8.0软删除需模型、字段、调用三者严丝合缝:数据库字段须为datetime/timestamp null且禁用默认值;模型必须显式引入softdelete trait、启用$usesoftdelete=true并精确匹配$deletetime字段名;所有操作必须通过模型方法(如destroy()或实例delete()),禁用db类直删。

ThinkPHP 8.0 的软删除不是“开了 trait 就自动生效”,而是模型、字段、调用三者必须严丝合缝——漏掉任意一环,delete() 就是物理删库,withTrashed() 查不到数据,restore() 直接报错或静默失败。
数据库字段必须为 NULL 且禁用默认值
软删除依赖一个可空的标记字段,MySQL 中必须定义为 DATETIME NULL 或 TIMESTAMP NULL,严禁 NOT NULL DEFAULT CURRENT_TIMESTAMP。否则新记录插入时 delete_time 已有值,框架会误判为“已删除”。
- 正确 SQL:
ALTER TABLE user ADD delete_time DATETIME NULL DEFAULT NULL; - 若用
BIGINT存时间戳(如deleted_at BIGINT UNSIGNED NULL),模型中必须声明类型:protected $type = ['deleted_at' => 'integer']; - 改完字段后,务必清空
runtime/cache/目录,否则模型仍读缓存旧结构,软删逻辑不触发
模型必须显式启用 SoftDelete 并配对字段名
TP8 使用 think\model\concern\SoftDelete,仅导入命名空间不够,类定义中必须同时写 use SoftDelete; 和 protected $useSoftDelete = true;。字段名不匹配或配置缺失,整个机制就断链。
- 导入和启用缺一不可:
use think\model\concern\SoftDelete;+use SoftDelete;+protected $useSoftDelete = true; - 若字段叫
is_deleted(布尔型),需配成数组:protected $deleteTime = ['is_deleted', 1];;恢复时写回0,不能为NULL - 若字段叫
deleted_at(时间型),直接写:protected $deleteTime = 'deleted_at'; - 模型若启用自动时间戳(
$autoWriteTimestamp = true),切勿把deleted_at加入$createTime或$updateTime,否则新增即被“删”
删除和查询必须走模型方法,Db 类完全绕过
Db::name('user')->where('id', 1)->delete() 或 $user->where('id', 1)->delete() 都会跳过软删逻辑,直连数据库执行物理删除——这是线上事故最高发场景。
- 软删唯一合法方式:
UserModel::destroy(1)(静态)或$user->delete()(实例) - 查全部含已删数据:
UserModel::withTrashed()->find(1);只查已删:UserModel::onlyTrashed()->select() - 关联查询不会继承父模型的软删状态,子模型要单独加:
->with(['posts' => function ($q) { $q->withTrashed(); }]) -
restore()仅支持实例方法,且必须先查出软删状态的记录:$user = UserModel::onlyTrashed()->find(1); $user?->restore();
字段类型与模型声明不一致会导致静默失效
比如数据库字段是 DATETIME,但模型中错误声明 protected $type = ['delete_time' => 'string'],框架内部比对失败,WHERE delete_time IS NULL 条件永远不生效,软删形同虚设。
-
DATETIME/TIMESTAMP字段:模型无需额外$type声明,保持默认即可 -
BIGINT字段:必须配protected $type = ['delete_time' => 'integer'];,否则写入时转字符串再强转 int,结果常为0 - 布尔/整型字段(如
is_deleted TINYINT):必须用数组形式声明$deleteTime,且restore()会写回0,不是NULL
最易忽略的是:字段约束、模型声明、调用路径这三处必须字面级一致——哪怕数据库字段是 deleted_at,模型里写成 deletedAt,或者 Db 和 Model 混用一次,整个软删就崩了,且无任何报错提示。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











