必须将withtrashed()置于查询链首位,如usermodel::withtrashed()->where('status', 1)->select(),否则因sql条件已固化而失效;需确保模型启用softdelete、字段名与类型配置正确,关联查询须单独调用withtrashed()。

必须用 withTrashed(),且它得是查询链第一个方法,否则查不到已软删除的数据。
withTrashed() 必须放在最前面,顺序错就白写
很多人写成 UserModel::where('status', 1)->withTrashed()->select(),结果还是没包含已删数据——因为 where() 先触发了条件构建,withTrashed() 后置完全无效。
-
withTrashed()的作用是移除默认的delete_time IS NULL条件,但它只在查询构建早期生效;一旦其他条件(如where()、order())先执行,SQL 框架已固化 WHERE 子句 - 正确写法只有这一种顺序:
UserModel::withTrashed()->where('status', 1)->select() - 不确定是否生效?加
->buildSql()看生成的 SQL,确认里面没有delete_time IS NULL这类条件
查不到数据?先检查模型和数据库字段是否对齐
即使调用了 withTrashed(),返回仍是空数组,大概率不是代码问题,而是底层配置没配对。
- 数据库字段必须存在且类型匹配:推荐
DATETIME NULL或BIGINT NULL;严禁NOT NULL DEFAULT '0000-00-00',MySQL 严格模式下写入null会失败 - 模型中必须声明:
use SoftDelete;+protected $deleteTime = 'delete_time';(若字段名不同,如deleted_at,此处必须同步) - 若字段是整型时间戳,还得加:
protected $type = ['delete_time' => 'integer'];,否则框架把null转成字符串再强转为0,仍被判定为“已删除”
关联查询里 withTrashed() 默认不传染
UserModel::withTrashed()->with('posts')->find(1) 只能让用户主记录包含已删的,但关联的 posts 依然受自身模型软删除规则限制——哪怕主模型开了 withTrashed(),子模型不显式处理,posts 仍是 null 或空数组。
- 解决办法一:在关联定义里硬编码
withTrashed(),例如:public function posts() { return $this->hasMany(Post::class)->withTrashed(); } - 解决办法二:查询时动态指定:
UserModel::with(['posts' => function ($q) { $q->withTrashed(); }])->find(1) - 前提条件:关联模型(如
Post)本身也启用了SoftDeletetrait,否则withTrashed()不起作用也不报错
性能隐患:withTrashed() 容易让 delete_time 索引失效
启用 withTrashed() 后,ThinkPHP 生成的 SQL 往往变成 WHERE (delete_time IS NULL OR delete_time IS NOT NULL) 或类似结构,MySQL 对 OR 条件优化差,极易退化为全表扫描。
- 高频查询(比如列表页)别长期开着
withTrashed(),只在回收站、审计导出等明确需要的场景按需开启 - 给
delete_time字段单独建索引:ALTER TABLE user ADD INDEX idx_delete_time (delete_time); - 如果业务上“已删数据”占比极高(比如 >30%),考虑分表或归档,而不是靠索引硬扛
最容易被忽略的一点:withTrashed() 只改查询行为,它不恢复数据、不触发事件、也不影响后续 update/restore 调用——查出来只是“看见”,要改还得另走逻辑。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











