软删除字段名必须为delete_time且类型为datetime,thinkphp 6硬编码绑定该字段,不支持自定义;启用softdelete后默认软删,delete(true)执行物理删除,查询自动过滤已软删数据,恢复需用onlytrashed()->restore()并手动刷新。

软删除字段名必须叫 delete_time
ThinkPHP 6 的软删除机制是硬编码绑定字段名的,不支持自定义字段(如 is_deleted 或 deleted_at)。如果你用的是 soft_delete 特性,模型里只要启用就会自动读写 delete_time 字段。这个字段必须是 datetime 类型(MySQL 中推荐用 DATETIME,不是 TIMESTAMP,避免时区隐式转换问题),且默认值设为 NULL。
常见错误现象:deleteTime 属性在模型里手动改了但没生效、查询时仍返回已“删”数据、调用 delete() 后数据库字段没更新——大概率是字段名不对或类型不匹配。
- 建表 SQL 示例:
ALTER TABLE user ADD delete_time DATETIME NULL DEFAULT NULL; - 模型中启用方式:
use think\model\concern\SoftDelete;+use SoftDelete;,别漏掉 trait - 字段名写错成
deleted_at或is_deleted:软删除完全不触发,delete()变成真删
delete() 默认只软删,真删要加参数
调用模型实例或查询器的 delete() 方法时,ThinkPHP 默认走软删除逻辑——前提是模型启用了 SoftDelete 且 delete_time 字段存在。想绕过软删执行物理删除,必须显式传参:
// 软删(默认行为)
UserModel::where('id', 123)->delete();
// 真删(force = true)
UserModel::where('id', 123)->delete(true);
// 模型实例上也一样
$user = UserModel::find(123);
$user->delete(); // 软删
$user->delete(true); // 真删
注意:delete(true) 不会清空 delete_time 字段,而是直接执行 DELETE SQL;如果字段缺失或类型异常,delete(true) 也可能静默失败,建议配合 getLastSql() 调试。
查询默认自动过滤软删数据,要查出来得手动放开
启用 SoftDelete 后,所有 select 类查询(get、find、select、paginate)都会自动加上 WHERE delete_time IS NULL 条件。这是全局行为,无法关闭,除非你主动干预。
需要查出包含已软删的数据时,有两个办法:
- 用
withTrashed():UserModel::withTrashed()->where('id', 123)->find() - 用
onlyTrashed():只查已软删的,比如做回收站列表:UserModel::onlyTrashed()->select()
这两个方法只能用于查询器(Query 对象),不能在模型静态方法如 find() 上直接链式调用,否则报错。另外,withTrashed() 不影响关联查询的软删过滤逻辑——关联模型是否过滤,取决于它自己的配置。
restore() 恢复操作有前提,且不触发事件
restore() 是把 delete_time 设回 NULL,但它只对当前模型实例或 Query 生效,且要求原记录的 delete_time 不为 NULL。常见坑点:
- 用
where()->restore()时,如果 where 条件没命中软删数据(即delete_time IS NOT NULL的行),不会报错也不会更新,静默跳过 -
restore()不触发before_restore/after_restore事件(ThinkPHP 6.0.x 至 6.3.x 均如此),业务中依赖事件做日志或同步的话,得自己补 - 恢复后不会自动重载模型属性,
$user->delete_time仍是旧值,需重新refresh()或再查一次
所以实际恢复逻辑建议写成:UserModel::onlyTrashed()->where('id', 123)->restore() && UserModel::find(123);,确保拿到最新状态。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











