thinkphp 8.1 软删除后查不到数据是正常机制,因默认自动添加 delete_time is null 条件;需确认模型配置、字段类型、显式调用 withtrashed()、排查全局作用域干扰及关联查询中子模型的软删处理。

ThinkPHP 8.1 软删除后数据“明明在库中却查不到”,不是数据丢了,而是框架默认自动过滤了已软删记录——这是软删除机制的正常行为,不是故障。关键要理解:启用 SoftDelete 后,所有常规查询(find、select、where)都会自动加上 delete_time IS NULL 条件,所以已删数据天然被屏蔽。
确认软删除字段配置是否生效
查不到已删数据,第一步不是调方法,而是验证底层是否真启用了软删逻辑:
- 检查模型是否正确定义了软删字段:
protected $deleteTime = 'delete_time';(字段名必须与数据库完全一致) - 确保该字段在数据库中类型为
DATETIME或TIMESTAMP,且允许 NULL(NULLABLE = YES),默认值必须是NULL,不能是CURRENT_TIMESTAMP或固定时间 - 运行
DESCRIBE your_table;直接查看字段定义;若不合规,用ALTER TABLE `your_table` MODIFY COLUMN `delete_time` DATETIME NULL DEFAULT NULL;修复
必须显式调用 withTrashed() 才能包含已删数据
框架不会“自动展示已删数据”,必须主动绕过过滤:
-
UserModel::withTrashed()->select()→ 查全部(含已删 + 未删) -
UserModel::onlyTrashed()->select()→ 只查已删的 -
这两个方法必须放在链式调用最前面,例如:
UserModel::withTrashed()->where('status', 1)->select()✅
而UserModel::where('status', 1)->withTrashed()->select()❌(顺序错,无效)
排查是否被全局作用域或自定义 scope 干扰
即使启用了软删除,如果模型重写了 baseScope() 或定义了其他全局 scope,可能二次添加 delete_time IS NULL 或冲突条件,导致已删数据仍被过滤:
- 临时注释掉模型中的
baseScope()方法,再试withTrashed() - 检查是否有
scopeXXX()方法里硬写了where('delete_time', null)类逻辑 - 可用
buildSql()输出实际 SQL,确认最终 WHERE 中是否还有残留的软删过滤条件
关联查询时主模型 withTrashed() 不影响子模型
如果查的是带关联的数据(如 User::with('posts')),即使 User 模型调了 withTrashed(),Post 关联数据仍按自身模型规则过滤(即默认不查已删的 Post):
- 在关联定义中加
->withTrashed():return $this->hasMany(Post::class)->withTrashed(); - 或查询时用闭包:
User::withTrashed()->with(['posts' => function($q) { $q->withTrashed(); }])->find(1) - 前提是 Post 模型也已正确启用 SoftDelete 并配置了对应字段
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











