thinkphp软删除需数据库字段(datetime/bigint null)、模型配置(显式use softdelete、$deletetime匹配、清缓存)和调用方式(仅模型destroy/delete、withtrashed查询、实例restore)三者严丝合缝,缺一即物理删除。

数据库字段必须是 DATETIME NULL 或 BIGINT NULL
软删除不是加个字段就能用,字段类型和约束直接决定逻辑是否生效。MySQL 里 delete_time 必须定义为 DATETIME NULL 或 TIMESTAMP NULL,严禁 NOT NULL DEFAULT CURRENT_TIMESTAMP。否则新记录插入时该字段就有值,框架会误判为“已删除”。
若用整型存时间戳(如 deleted_at BIGINT UNSIGNED NULL),模型中必须声明类型:protected $type = ['deleted_at' => 'integer'];,否则写入 null 会被转成字符串再强转为 0,后续仍被识别为已删。
改完字段后,务必手动清空 runtime/cache/ 目录,否则模型读缓存旧结构,软删逻辑完全不触发。
模型必须显式启用 SoftDelete 且字段名完全匹配
仅 use think\model\concern\SoftDelete; 导入命名空间不够,类定义里必须同时写:use SoftDelete; + protected $useSoftDelete = true;。$deleteTime 字段名必须与数据库字段名一字不差:数据库是 deleted_at,就得写 protected $deleteTime = 'deleted_at';;若是布尔型 is_deleted,则配成数组:protected $deleteTime = ['is_deleted', 1];。
如果模型启用了自动时间戳($autoWriteTimestamp = true),切勿把 deleted_at 加进 $createTime 或 $updateTime,否则新增记录时就被“删”了。
withTrashed() 只影响查询,restore() 才真正改状态
withTrashed() 和 onlyTrashed() 是查询修饰器,不是恢复指令——它们只控制本次 SQL 的 WHERE 条件,调用位置必须在 find() 或 select() 之前,写成 User::where()->withTrashed()->find() 无效。
恢复数据必须分两步:
• 先查出软删记录:User::onlyTrashed()->find(123)(不能用 where()->find(),默认已被过滤)
• 再调实例方法:$user->restore()(User::restore() 或 User::where()->restore() 全部报错)restore() 静默失败(返回 false 而非抛异常),常见原因:字段不允许 NULL、类型不匹配、或记录根本不存在。务必检查返回值。
批量恢复没有 restoreAll(),别信网上抄的代码
TP8 没有 restoreAll() 方法,任何 User::restoreAll() 写法都会直接报错:Call to undefined method。
两种安全路径:
• 要触发 restoring/restored 事件:用 User::onlyTrashed()->chunk(500, function ($items) { $items->each->restore(); });
• 只求快、不关心事件:用 Db::name('user')->whereNotNull('delete_time')->update(['delete_time' => null]);
关联模型不会自动恢复,withTrashed() 不透传,restore() 不级联。用户恢复了,订单仍软删,得单独查 $user->orders()->onlyTrashed()->select() 再逐条处理。大数量时建议事务包裹,避免主从状态不一致。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











