softdelete 默认自动过滤 delete_time 非空记录,需用 withtrashed() 查软删数据,onlytrashed() 仅查已删数据;关联查询需单独调用 withtrashed();字段类型为 int 时须配置 $type 声明。

ThinkPHP 模型开启 SoftDelete 后查不到软删数据?
默认情况下,SoftDelete 会自动过滤掉 delete_time 非空的记录——这不是 bug,是设计行为。只要模型用了 use SoftDelete;,所有常规查询(select、find、where)都会自动加 AND delete_time IS NULL 条件。
常见错误现象:Db::name('user')->where('id', 1)->find() 返回 null,但数据库里该行 delete_time 是时间戳而非 null,人还在表里,只是被“逻辑藏起来了”。
- 必须显式调用
withTrashed()才能查到已软删的数据 -
withTrashed()只影响当前链式调用,不改变模型全局行为 - 它不会恢复数据,只是绕过软删过滤,和
force(true)作用不同 - 若字段名不是默认的
delete_time,需在模型中通过protected $deleteTime = 'deleted_at';显式声明
withTrashed() 和 onlyTrashed() 的区别在哪
两者都用于突破软删限制,但语义和生成的 SQL 完全不同:
-
withTrashed()→ 移除delete_time IS NULL条件,查全部(含已删 + 未删) -
onlyTrashed()→ 替换为delete_time IS NOT NULL,只查已删的 - 它们不能连用,
onlyTrashed()->withTrashed()会以后者为准(即查全部) - 在关联查询中,父模型调用
withTrashed()不会影响子模型的软删过滤,子模型需单独加
示例:User::withTrashed()->find(1) 查 id=1 的用户(无论是否软删);User::onlyTrashed()->find(1) 只有当该用户确实被软删过才返回结果。
关联模型里怎么查带软删的关联数据
ThinkPHP 的关联方法(如 hasMany、belongsTo)默认不继承主模型的软删状态。即使你在主模型调用了 withTrashed(),关联数据仍按自身模型规则过滤。
- 要在关联中也包含软删数据,得在定义关联时用
->withTrashed(),例如:public function posts() { return $this->hasMany(Post::class)->withTrashed(); } - 或者查询时动态追加:
User::with(['posts' => function ($q) { $q->withTrashed(); }])->find(1) - 注意:如果
Post模型没启用SoftDelete,withTrashed()无效,也不会报错 - 性能上,多一层
withTrashed()就多一个OR delete_time IS NULL或类似条件,大表要注意索引覆盖delete_time
软删除字段类型选 datetime 还是 int?
ThinkPHP 默认用 datetime 类型存 delete_time,但实际项目中常有人改成 int(时间戳)。这本身可行,但必须同步改模型配置,否则 SoftDelete 行为异常。
- 若数据库字段是
int,必须在模型中写:protected $deleteTime = 'delete_time'; protected $type = ['delete_time' => 'integer']; - 漏掉
$type声明会导致 ThinkPHP 把时间戳当字符串处理,withTrashed()查询可能因类型隐式转换失效 - MySQL 8.0+ 对
datetime和int的索引效率差异极小,优先保持默认datetime更省心 - 迁移已有数据时,别直接改字段类型,先确保所有软删逻辑已测试通过
最常被忽略的一点:软删除不是“开关”,而是查询拦截器——它不改 SQL 语法,只悄悄拼条件。所以任何绕过 ORM 直接写 Db::table() 或原生查询的地方,都不会受 SoftDelete 影响,得手动处理 delete_time 字段。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










