如何调试ThinkPHP中软删除数据查询不到的模型查询条件问题?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1025个文字,预计阅读时间需要5分钟。
ThinkPHP的软删除功能默认仅对`select`、`find`等类查询自动过滤掉`delete_time !=NULL`的数据。如果手动添加了`where('delete_time', 'null')`或类似条件,可能会与软删除机制冲突。这是因为实际数据库中的`NULL`值不能与字符串`'null'`或数字`0`或空字符串进行比较,导致冲突。
常见错误写法:where('delete_time', 'null')、where('delete_time', '')、where('delete_time', 0),这些全都不匹配数据库里的 NULL 值。
- 查“已软删除”的记录,必须用
whereNull('delete_time')(查未删)或whereNotNull('delete_time')(查已删) - 若模型启用了软删除(
use SoftDelete;),默认所有select都带where delete_time is null,想绕过它得显式调用withTrashed() -
withoutTrashed()是默认行为,一般不用显式写;但onlyTrashed()等价于whereNotNull('delete_time')
withTrashed() 不起作用?检查模型是否真正启用软删除
光在模型里 use SoftDelete; 不够,还必须定义 $deleteTime 属性,否则框架不认为该模型支持软删除,withTrashed() 就只是个空方法,不会修改查询条件。
典型缺失配置:
立即学习“PHP免费学习笔记(深入)”;
class User extends Model { use SoftDelete; // ❌ 缺少这行,软删除机制不激活 protected $deleteTime = 'delete_time'; }
-
$deleteTime必须是字符串,如'delete_time';设为false或留空会禁用软删除 - 字段类型建议用
datetime或timestamp,避免用int存时间戳再手动赋值,容易因时区/格式导致NULL判断失败 - 如果表中该字段允许
NULL但默认值设成了0或'0000-00-00 00:00:00',那whereNotNull也查不到——得先清理脏数据
调试实际执行的 SQL:看 delete_time 条件到底有没有加进去
别猜,直接看框架生成的 SQL。ThinkPHP 6+ 可用 getLastSql(),TP5 用 getRealSql(),配合 fetchSql(true) 强制不执行只返回 SQL。
例如:
$list = User::withTrashed()->where('status', 1)->fetchSql(true)->select(); echo $list; // 输出类似:SELECT * FROM `user` WHERE `status` = 1 AND `delete_time` IS NOT NULL
- 如果输出里没有
delete_time相关条件,说明软删除未启用,或调用了错误的方法(比如在withTrashed()前用了where且模型未初始化软删除) - 注意:
fetchSql(true)返回的是字符串,不是结果集,别把它当数据用 - TP6 中
Db::name('user')->withTrashed()->select()也有效,但必须确保是模型查询入口,原生 Db 查询不识别软删除逻辑
关联查询中软删除失效:子模型没启用 or 关联没透传
主模型调了 withTrashed(),但关联的 has 或 belongsToMany 查出来的子记录仍是“被过滤后”的,因为关联查询默认不继承主模型的软删除策略。
- 一对一/一对多关联:在关联方法里显式加
->withTrashed(),例如return $this->hasMany('Order')->withTrashed(); - 多对多中间表如果也用了软删除,需在中间模型里单独启用,并在关联定义中指定中间模型类
- 使用
with(['orders' => function ($q) { $q->withTrashed(); }])才能控制关联查询是否包含已删除项 - 切记:软删除是模型级行为,不是全局 DB 行为,每个参与查询的模型都得自己配好
$deleteTime和SoftDelete
软删除最易忽略的点在于“多层嵌套时各模型自治”,一个没配,整条链就断在那儿了。
本文共计1025个文字,预计阅读时间需要5分钟。
ThinkPHP的软删除功能默认仅对`select`、`find`等类查询自动过滤掉`delete_time !=NULL`的数据。如果手动添加了`where('delete_time', 'null')`或类似条件,可能会与软删除机制冲突。这是因为实际数据库中的`NULL`值不能与字符串`'null'`或数字`0`或空字符串进行比较,导致冲突。
常见错误写法:where('delete_time', 'null')、where('delete_time', '')、where('delete_time', 0),这些全都不匹配数据库里的 NULL 值。
- 查“已软删除”的记录,必须用
whereNull('delete_time')(查未删)或whereNotNull('delete_time')(查已删) - 若模型启用了软删除(
use SoftDelete;),默认所有select都带where delete_time is null,想绕过它得显式调用withTrashed() -
withoutTrashed()是默认行为,一般不用显式写;但onlyTrashed()等价于whereNotNull('delete_time')
withTrashed() 不起作用?检查模型是否真正启用软删除
光在模型里 use SoftDelete; 不够,还必须定义 $deleteTime 属性,否则框架不认为该模型支持软删除,withTrashed() 就只是个空方法,不会修改查询条件。
典型缺失配置:
立即学习“PHP免费学习笔记(深入)”;
class User extends Model { use SoftDelete; // ❌ 缺少这行,软删除机制不激活 protected $deleteTime = 'delete_time'; }
-
$deleteTime必须是字符串,如'delete_time';设为false或留空会禁用软删除 - 字段类型建议用
datetime或timestamp,避免用int存时间戳再手动赋值,容易因时区/格式导致NULL判断失败 - 如果表中该字段允许
NULL但默认值设成了0或'0000-00-00 00:00:00',那whereNotNull也查不到——得先清理脏数据
调试实际执行的 SQL:看 delete_time 条件到底有没有加进去
别猜,直接看框架生成的 SQL。ThinkPHP 6+ 可用 getLastSql(),TP5 用 getRealSql(),配合 fetchSql(true) 强制不执行只返回 SQL。
例如:
$list = User::withTrashed()->where('status', 1)->fetchSql(true)->select(); echo $list; // 输出类似:SELECT * FROM `user` WHERE `status` = 1 AND `delete_time` IS NOT NULL
- 如果输出里没有
delete_time相关条件,说明软删除未启用,或调用了错误的方法(比如在withTrashed()前用了where且模型未初始化软删除) - 注意:
fetchSql(true)返回的是字符串,不是结果集,别把它当数据用 - TP6 中
Db::name('user')->withTrashed()->select()也有效,但必须确保是模型查询入口,原生 Db 查询不识别软删除逻辑
关联查询中软删除失效:子模型没启用 or 关联没透传
主模型调了 withTrashed(),但关联的 has 或 belongsToMany 查出来的子记录仍是“被过滤后”的,因为关联查询默认不继承主模型的软删除策略。
- 一对一/一对多关联:在关联方法里显式加
->withTrashed(),例如return $this->hasMany('Order')->withTrashed(); - 多对多中间表如果也用了软删除,需在中间模型里单独启用,并在关联定义中指定中间模型类
- 使用
with(['orders' => function ($q) { $q->withTrashed(); }])才能控制关联查询是否包含已删除项 - 切记:软删除是模型级行为,不是全局 DB 行为,每个参与查询的模型都得自己配好
$deleteTime和SoftDelete
软删除最易忽略的点在于“多层嵌套时各模型自治”,一个没配,整条链就断在那儿了。

