ThinkPHP 8.0批量插入时软删除字段不会自动设为NULL,需确保数据库字段允许NULL且无默认值、模型显式启用SoftDelete并匹配字段名、禁用时间戳干扰,插入时显式赋null或用saveAll触发模型逻辑。

ThinkPHP 8.0 批量添加数据时,软删除字段(如 delete_time)**不会自动赋值为 NULL**,除非你明确配置并确保模型、字段类型、缓存三者协同生效。否则新插入的记录可能因该字段被误设为非空值(比如 0 或默认时间),导致后续查询时被错误识别为“已删除”。
数据库字段必须允许 NULL 且禁用默认值
这是最易忽略的前提:
-
字段类型只能是
DATETIME NULL或BIGINT NULL(不能是NOT NULL,更不能带DEFAULT CURRENT_TIMESTAMP) - MySQL 建表语句示例:
ALTER TABLE `user` ADD `delete_time` DATETIME NULL COMMENT '软删除时间'; - 若用整型存时间戳(如
deleted_at BIGINT UNSIGNED NULL),模型中必须声明类型映射:protected $type = ['deleted_at' => 'integer'];
模型必须显式启用 SoftDelete 并匹配字段名
仅引入 trait 不够,需完整声明:
- 在模型类中写全:
use think\model\concern\SoftDelete;<br>use SoftDelete;
- 字段名严格一致:
protected $deleteTime = 'delete_time';(与数据库字段名一字不差) - 禁用自动时间戳对软删字段的干扰:
不要把delete_time加入$createTime或$updateTime,否则新增时它会被自动写入当前时间 → 立刻变“已删”
批量插入时软删字段保持 NULL 的实操方式
ThinkPHP 的 insert() 和 insertAll() 默认不处理软删除逻辑,也不会主动写入 NULL。要确保字段为空,有三种可靠做法:
立即学习“PHP免费学习笔记(深入)”;
-
方式一:插入前显式指定为 null
批量数据中每条都带上'delete_time' => null:User::insert([<br> ['name' => '张三', 'delete_time' => null],<br> ['name' => '李四', 'delete_time' => null]<br>]);
-
方式二:用模型实例 + saveAll(触发模型逻辑)
虽不走验证器,但会经过模型字段类型转换:$users = array_map(fn($d) => new User($d), $data);<br>User::saveAll($users);
前提是模型已正确配置$type和SoftDelete,否则仍无效 -
方式三:清空 runtime 缓存后重试
改完数据库字段或模型配置后,务必手动删除runtime/cache/目录
否则模型读取旧缓存结构,delete_time字段可能被忽略或类型误判
常见静默失败原因排查
插入后发现数据查不到,或刚插入就被当成已删,大概率是以下问题:
- 数据库字段定义为
NOT NULL DEFAULT '0000-00-00 00:00:00'→ 新记录一入库delete_time就有值 → 框架判定为已删 - 模型未声明
use SoftDelete;,只写了use think\model\concern\SoftDelete;→ 软删完全不生效,但插入不受影响 - 字段名大小写不一致(如数据库是
deleted_at,模型写成deleteTime)→ 匹配失败,字段被跳过 - 使用
Db::name('user')->insertAll()→ 绕过模型,不走任何软删逻辑,字段按 SQL 原样写入(若没传值,由数据库默认值兜底,风险极高)



















