thinkphp5新增数据须注意模型状态:单条用save()并显式isupdate(false),多条用saveall()且确保无主键字段;主键名需匹配,id从模型属性读取而非返回值;外部数据入库前必须allowfield过滤。

ThinkPHP5 新增数据的核心就两条:单条用 save(),多条用 saveAll();但不注意主键和模型状态,很容易重复写入或拿不到 ID。
单条新增必须注意模型的 isUpdate 状态
很多人第一次调用 $user->save() 成功了,第二次再调用却报 SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '57' for key 'PRIMARY'。根本原因是:模型实例默认会记住上一次写入的主键(比如 id=57),第二次没重置状态时,save() 会误判为“更新”,而你又没传 where 条件,结果尝试用原主键再插一次。
- 正确做法是显式声明「这是新增」:
$user->isUpdate(false)->save($data) - 如果已经赋过值(比如
$user->name = 'xxx'),再调save()前也得加isUpdate(false),否则模型仍可能沿用旧主键 - 不要在同一个模型实例上反复
save()—— 每次新增建议新建实例,或至少调用$user->exists(false)清除存在标记
获取自增 ID 不能只看 $user->id
save() 执行后,模型的主键属性(如 id 或 user_id)会自动被赋值,但这个值**只在当前模型实例中有效**,且依赖主键字段名是否匹配。如果你的表主键是 uid,而代码里写 echo $user->id,就会输出空或 0。
- 安全写法是先确认主键名:
$pk = $user->getPk(),再取值:$user->$pk - 如果模型设置了
pk属性(如protected $pk = 'uid';),那$user->uid才是真实 ID - 注意:
$user->save()返回的是影响行数(通常是 1),不是 ID;ID 必须从模型属性读
saveAll() 批量新增时主键字段要小心
saveAll() 默认会检查每条数据是否含主键:有主键就走更新,没主键才新增。所以如果你传入的数组里带了 'id' => 1 这类字段,哪怕只是占位,也会被当成更新操作,导致部分数据被忽略或报错。
- 纯新增场景,确保每条数据都不含主键字段(删掉
id、uid等) - 真要带主键批量插入(比如迁移数据),得加第二个参数
false:$user->saveAll($list, false),关闭自动识别逻辑 -
saveAll()返回的是模型对象数组(V5.0.13+ 受resultSetType影响),每项都有自己的主键值,可直接遍历取$item->id
外部数据(如 $_POST)入库前必须过滤字段
直接把 $_POST 丢给 save() 很危险:攻击者可能提交非法字段(如 status=999、is_admin=1),绕过业务逻辑写入数据库。
- 最简方案:
$user->allowField(true)->save($_POST),自动过滤掉非数据表字段 - 更可控的做法:
$user->allowField(['name', 'email', 'phone'])->save($_POST),白名单明确指定允许字段 - 注意:
allowField(true)依赖模型的$schema或数据库实际字段结构,表结构变更后需同步更新缓存(runtime/cache/)
真正容易被忽略的是模型实例的“记忆性”——它不像 DB 查询那样无状态。一次 save() 后,模型内部的 isUpdate、exists、data 全部保留,下次操作若不重置,行为就不可控。别图省事复用实例,宁可多写一行 new User()。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











