ci4 的 model::update() 必须显式传 id(如 $model->update($id, $data)),否则默认按主键更新整表;需严格配置 $allowedfields 白名单,返回值为影响行数或 false,不可用 if 直接判断。

CI4 的 Model::update() 不是“传数据就能更新”,它默认按主键更新,不传 ID 或没配 $primaryKey 会静默更新整张表——这是线上事故高发点。
Model::update() 必须显式传 ID,否则更新整表
CI4 的 update() 方法底层逻辑是「先查主键字段名,再拼 WHERE `id` = ?」。如果你只写 $model->update($data),它会把 $data 应用到全表所有行。
- 常见错误现象:
update()执行后没报错、日志无 SQL、但整张表的指定字段全变成同一个值 - 必须写成
$model->update($id, $data),$id是具体数值(如123),不能是数组或空值 - 如果 ID 来自表单,务必用
$this->request->getPost('id')获取,并校验非空:if (empty($id)) { throw new \RuntimeException('ID required'); } - 若想按其他条件更新(比如
WHERE status = 'pending'),别碰update(),改用 Query Builder:$model->builder()->where('status', 'pending')->update($data)
$allowedFields 是硬性白名单,漏配字段就静默失败
$allowedFields 不是可选项,是安全开关。只要要更新的字段名不在这个数组里,update() 直接返回 false,不抛异常、不写日志、也不执行 SQL。
- 模型中必须明确定义:
protected $allowedFields = ['anotasi', 'updated_at'];,字段名严格匹配数据库列名(区分大小写) - 开发调试时可临时设为
['*'],但上线前必须删掉——CI4 不支持通配符,设成['*']实际上会让update()拒绝所有字段 - 单选按钮提交的字段(如
anotasi)最容易被忽略:前端传Positif,但模型没把它加进$allowedFields,结果 update 始终返回false
单选按钮值要校验 + 类型转换,不能直接塞进 update
前端 <input type="radio" name="anotasi" value="Positif"> 提交的是字符串,但数据库字段可能是 TINYINT、ENUM 或 VARCHAR。类型不一致会导致更新失败或存入意外值。
- 先校验合法性:
$value = $this->request->getPost('anotasi'); if (! in_array($value, ['Positif', 'Negatif', 'Netral'])) { $value = 'Netral'; } - 如果字段是整型(如
1/0),必须手动转换:'anotasi' => (int) ($value === 'Positif') - 用户可能完全不选,
$this->request->getPost('anotasi')返回null,直接传给update()可能触发空值插入或验证失败 - 避免在控制器里拼 SQL 或手动构造数组,清洗后的干净数据才交给模型
update() 返回值不是布尔值,别用 if 判断成功与否
Model::update() 返回的是影响行数(整数)或 false(执行失败)。用 if ($model->update($id, $data)) 会把影响行数为 0(比如 ID 不存在)误判为失败,或者把影响行数为 1 当作成功,掩盖真实问题。
- 正确做法是判断是否为
false:if ($model->update($id, $data) === false) { // 处理失败 } - 若需确认是否真有数据被改,检查返回值是否大于
0:$affected = $model->update($id, $data); if ($affected === false || $affected === 0) { /* ID 不存在或没变化 */ } - 容易被忽略的点:时间戳字段(如
updated_at)自动更新后,即使其他字段值没变,也会计为「影响 1 行」;所以$affected === 0并不总代表失败











