thinkphp软删除不自动透传至关联模型,需在关联定义或with闭包中显式调用withtrashed(),且子模型必须启用softdelete trait并正确配置delete_time字段;无级联恢复机制,需手动处理并包裹事务。

关联查询时软删除不生效,主模型 withTrashed() 没用
ThinkPHP 的 withTrashed() 只作用于当前模型,不会自动透传到关联模型。比如 User::withTrashed()->with('posts')->find(1) 能查出已软删的用户,但 posts 仍是按默认规则过滤——若文章被软删了,posts 数组就是空的。
常见错误是以为“主模型开了软删,关联自然就跟着走”,结果调试半天发现数据丢了。
- 必须在关联定义里显式加
withTrashed():return $this->hasMany(Post::class)->withTrashed(); - 或查询时动态指定:
User::with(['posts' => function ($q) { $q->withTrashed(); }])->find(1) - 如果
Post模型没启用SoftDeletetrait,withTrashed()无效且不报错,得先确认子模型也配好了 - 注意:关联方法中写
->withTrashed()后,该关联在所有调用中都默认包含软删数据,除非你额外加withoutGlobalScopes()或onlyTrashed()
关联模型没启软删除,但想查它的已删记录
父模型启用 SoftDelete 不会“传染”给关联模型,子模型是否软删、怎么查,完全由它自己的配置决定。如果你的 Post 模型没写 use SoftDelete;,那它压根没有软删逻辑,withTrashed() 就只是个空操作。
此时即使数据库里 post 表有 delete_time 字段,框架也不会自动识别或过滤。
- 先确保子模型类引入并声明:
use think\model\concern\SoftDelete;+use SoftDelete; - 检查字段名是否匹配:
protected $deleteTime = 'delete_time';(别拼错) - 字段类型必须是可空的
DATETIME或INT,且值非NULL才算“已删” - 如果子模型字段叫
is_deleted,必须同步配protected $type = ['is_deleted' => 'integer'];,否则delete()写进去的是字符串"1",查的时候永远不命中
软删后想级联恢复关联数据
ThinkPHP 没有内置的级联软删/恢复机制。用户软删了,文章不会自动软删;用户恢复了,文章也不会自动恢复。这是设计如此,不是 bug。
如果你需要“恢复用户同时恢复其所有文章”,得手动处理,不能依赖模型自动行为。
- 先查出已软删的用户:
$user = User::onlyTrashed()->find($id); - 再恢复用户:
$user->restore(); - 接着手动恢复关联数据:
Post::where('user_id', $id)->onlyTrashed()->update(['delete_time' => null]); - 或者更稳妥地逐条恢复(能触发子模型的事件):
Post::onlyTrashed()->where('user_id', $id)->select()->each(fn($p) => $p->restore()); - 注意事务包裹:上述多步操作建议包在
Db::transaction()里,避免只恢复了一半
关联预载 with() 里混用 withTrashed() 和 where 条件
在闭包里写 with(['posts' => function ($q) { $q->withTrashed()->where('status', 1); }]) 是合法的,但要注意条件叠加顺序和语义。
withTrashed() 移除的是 delete_time IS NULL,而 where('status', 1) 是额外加的 AND 条件,最终 SQL 是 WHERE (delete_time IS NULL OR delete_time IS NOT NULL) AND status = 1 —— 也就是查所有状态为 1 的文章,不管删没删。
- 如果只想查“已被软删且 status=1”的文章,该用
onlyTrashed():$q->onlyTrashed()->where('status', 1) - 如果用了
withTrashed()又加了where('delete_time', 'null'),会生成IS NULL条件,跟软删本意冲突,且可能被框架二次过滤 - 闭包里的查询构造器独立于主模型,所以
$q->withTrashed()只影响这次预载,不影响其他地方 - 大表慎用:
withTrashed()去掉过滤后数据量变大,记得给delete_time字段建索引,尤其是配合where多条件时
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











