软删除需模型、字段、调用三者对齐才生效;必须引入正确命名空间的softdelete trait、显式声明use softdelete、设置$usesoftdelete=true,delete_time字段须为datetime null或正确配置type,查询默认过滤软删数据,恢复需先查出再调用实例restore()。

软删除不是加个 trait 就自动生效,必须模型、字段、调用方式三者对齐,缺一不可;否则 delete() 会直接物理删除,数据静默丢失。
SoftDelete trait 怎么引入才真正起作用
只写 use SoftDelete; 不够,还必须确保:
- 模型类已
use think\model\concern\SoftDelete;(注意命名空间) - 类定义中显式声明
use SoftDelete;(不是use \think\model\concern\SoftDelete;) - 配置
protected $useSoftDelete = true;(TP6 必须显式开启,仅 trait 不触发行为) - 若模型启用了
$autoWriteTimestamp = true,切勿把delete_time加进$createTime或$updateTime—— 否则新增时就写入时间,刚插进去就“被删”
delete_time 字段建错会导致软删静默失败
字段名和类型不匹配,delete() 看似执行成功,实际却写入了非法值(比如空字符串、0、或当前时间),后续查询永远过滤掉它——但你根本收不到报错。
- MySQL 中必须建为
delete_time DATETIME NULL DEFAULT NULL(不能NOT NULL,也不能DEFAULT CURRENT_TIMESTAMP) - 若用
INT存时间戳,模型里必须加protected $type = ['delete_time' => 'integer'];,否则 PHP 强转后常为 0 - 字段名不是
delete_time?那就得在模型里明确写protected $deleteTime = 'is_deleted';,且该字段值必须能被识别为“已删”(如1或非空时间)
查不到软删数据?默认就是故意不让你看到
启用 SoftDelete 后,UserModel::select()、UserModel::find(1)、UserModel::where(...)->select() 全部自动追加 WHERE delete_time IS NULL。这不是 bug,是设计。
- 要查全部(含已删):必须链式调用
withTrashed(),且必须在select()或find()前 - 只查已删的:用
onlyTrashed(),等价于WHERE delete_time IS NOT NULL -
withTrashed()不会透传到关联模型,比如User::withTrashed()->with('posts')中的posts仍按自身规则过滤,需单独加->withTrashed() - 别用
Db::name('user')->where(...)->select()查软删数据——它绕过模型,完全不走 SoftDelete 逻辑
restore() 恢复失败不报错,容易误以为成功
restore() 是实例方法,调用前必须先查出软删状态的记录,且返回值需手动判断。
- 错误写法:
UserModel::where('id', 123)->restore()→ 报错:Call to undefined method - 正确流程:
$user = UserModel::onlyTrashed()->find(123); if ($user) { $result = $user->restore(); if (false === $result) { /* 失败,可能是字段被手动改过 */ } } -
restore()不触发save钩子,也不会重置delete_time字段值(它只是设为NULL) - 想彻底物理删除某条已软删记录?先
restore(),再force()->delete()或destroy($id, true)
最常被忽略的是:关联数据完全不参与软删除生命周期。用户软删了,它的订单、日志、附件不会自动标记,也不会自动恢复——这部分必须手写逻辑补全,没有“级联软删”这种内置机制。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











