Laravel模型软删除后,如何进行基于deleted_at时间查询的筛选操作?

2026-04-24 16:562阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计812个文字,预计阅读时间需要4分钟。

Laravel模型软删除后,如何进行基于deleted_at时间查询的筛选操作?

由于 Laravel 的软删除模型默认会自动过滤掉已软删除的记录,你无需手动添加 `whereNotNull('deleted_at')`。这样做不仅不会解决问题,反而会被 Eloquent 的全局作用域所覆盖。正确的做法是使用 Eloquent 提供的软删除功能,让 Eloquent 自动处理这些记录。

  • 软删除模型(用了 SoftDeletes trait)的所有查询,默认都带 whereNull('deleted_at')
  • whereNotNull('deleted_at') 被追加后,实际执行的是 WHERE deleted_at IS NULL AND deleted_at IS NOT NULL——永远为 false
  • 必须先调用 withTrashed()onlyTrashed() 解除全局作用域,再加条件

withTrashed()onlyTrashed() 的区别与选法

两者都绕过软删除的全局作用域,但行为不同:前者包含未删 + 已删,后者只取已删。别凭名字猜,得看你要什么数据。

  • withTrashed():查全部,deleted_atNULL 表示正常,有值表示软删除
  • onlyTrashed():只查 deleted_at IS NOT NULL 的记录,等价于手动写 whereNotNull('deleted_at'),但更安全
  • 如果只想查“已被软删除且创建时间在某天之后”,该用 onlyTrashed()->where('created_at', '>', '2024-01-01')

按软删除时间范围筛选的正确写法

软删除时间字段 deleted_at 是可空的 datetime,直接 whereBetween('deleted_at', [...]) 会漏掉 NULL 值,但你通常只关心“有值”的那些——所以得先锁定范围,再确保字段非空。

  • 查“上周被软删除的用户”:User::onlyTrashed()->whereBetween('deleted_at', ['2024-04-01', '2024-04-07'])->get()
  • 查“软删除时间在某个时间段内,且邮箱含 gmail 的记录”:User::onlyTrashed()->whereBetween('deleted_at', [...])->where('email', 'like', '%@gmail.com')->get()
  • 别用 withTrashed()->whereNotNull('deleted_at')->whereBetween(...):多此一举,onlyTrashed() 已隐含非空

软删除查询中容易忽略的时区和精度问题

deleted_at 默认是数据库当前时间戳,但 PHP、MySQL、应用配置的时区不一致时,查出来的“时间范围”可能错位几小时;另外 MySQL 5.6 及以下不支持微秒,Laravel 写入时若用了 useCurrentOnUpdate(),可能存成 000000 微秒,导致 whereBetween 匹配失败。

  • 确认 config/database.php 中对应连接的 'timezone' => '+00:00' 与 DB 实际时区一致
  • 开发环境用 MySQL 8.0+,或显式在迁移中声明精度:$table->softDeletes(6);(6 位微秒)
  • 线上 MySQL 版本低于 5.6 时,避免依赖微秒级时间比较,改用秒级 whereDate('deleted_at', '=','2024-04-01')
事情说清了就结束。软删除不是简单加个 where 条件的事,关键是理解 withTrashed() 这类方法如何与全局作用域协作,以及 deleted_at 字段本身的空值语义和存储精度限制。
标签:Laravel

本文共计812个文字,预计阅读时间需要4分钟。

Laravel模型软删除后,如何进行基于deleted_at时间查询的筛选操作?

由于 Laravel 的软删除模型默认会自动过滤掉已软删除的记录,你无需手动添加 `whereNotNull('deleted_at')`。这样做不仅不会解决问题,反而会被 Eloquent 的全局作用域所覆盖。正确的做法是使用 Eloquent 提供的软删除功能,让 Eloquent 自动处理这些记录。

  • 软删除模型(用了 SoftDeletes trait)的所有查询,默认都带 whereNull('deleted_at')
  • whereNotNull('deleted_at') 被追加后,实际执行的是 WHERE deleted_at IS NULL AND deleted_at IS NOT NULL——永远为 false
  • 必须先调用 withTrashed()onlyTrashed() 解除全局作用域,再加条件

withTrashed()onlyTrashed() 的区别与选法

两者都绕过软删除的全局作用域,但行为不同:前者包含未删 + 已删,后者只取已删。别凭名字猜,得看你要什么数据。

  • withTrashed():查全部,deleted_atNULL 表示正常,有值表示软删除
  • onlyTrashed():只查 deleted_at IS NOT NULL 的记录,等价于手动写 whereNotNull('deleted_at'),但更安全
  • 如果只想查“已被软删除且创建时间在某天之后”,该用 onlyTrashed()->where('created_at', '>', '2024-01-01')

按软删除时间范围筛选的正确写法

软删除时间字段 deleted_at 是可空的 datetime,直接 whereBetween('deleted_at', [...]) 会漏掉 NULL 值,但你通常只关心“有值”的那些——所以得先锁定范围,再确保字段非空。

  • 查“上周被软删除的用户”:User::onlyTrashed()->whereBetween('deleted_at', ['2024-04-01', '2024-04-07'])->get()
  • 查“软删除时间在某个时间段内,且邮箱含 gmail 的记录”:User::onlyTrashed()->whereBetween('deleted_at', [...])->where('email', 'like', '%@gmail.com')->get()
  • 别用 withTrashed()->whereNotNull('deleted_at')->whereBetween(...):多此一举,onlyTrashed() 已隐含非空

软删除查询中容易忽略的时区和精度问题

deleted_at 默认是数据库当前时间戳,但 PHP、MySQL、应用配置的时区不一致时,查出来的“时间范围”可能错位几小时;另外 MySQL 5.6 及以下不支持微秒,Laravel 写入时若用了 useCurrentOnUpdate(),可能存成 000000 微秒,导致 whereBetween 匹配失败。

  • 确认 config/database.php 中对应连接的 'timezone' => '+00:00' 与 DB 实际时区一致
  • 开发环境用 MySQL 8.0+,或显式在迁移中声明精度:$table->softDeletes(6);(6 位微秒)
  • 线上 MySQL 版本低于 5.6 时,避免依赖微秒级时间比较,改用秒级 whereDate('deleted_at', '=','2024-04-01')
事情说清了就结束。软删除不是简单加个 where 条件的事,关键是理解 withTrashed() 这类方法如何与全局作用域协作,以及 deleted_at 字段本身的空值语义和存储精度限制。
标签:Laravel