先查后更是保障数据一致性的关键实践,需在模型封装校验逻辑、控制器串联流程、事务保障原子性,并规避手动拼条件、时间戳遗漏等常见陷阱。

先查后更,是防止误更新、保障数据一致性的重要习惯。CI框架里实现这个逻辑,核心在于“查得准、判得清、更得稳”——不是单纯调用两次数据库方法,而是把校验逻辑嵌入流程中,避免竞态和空指针问题。
1. 在模型中封装查询+校验逻辑
不要在控制器里拼凑条件判断,把校验规则收拢到模型方法中,提高复用性和可测性。
- 定义一个如 get_by_id_for_update($id) 的方法:只查出需要更新的那条记录,并确保它存在、状态合法(比如 status != 'deleted')
- 返回完整数据数组或 FALSE;若为 FALSE,控制器直接终止流程,不走更新分支
- 示例中可加入业务校验,如:if (empty($row) || $row['status'] === 'locked') { return FALSE; }
2. 控制器中串联查询与更新动作
控制器负责流程编排,但不处理具体字段映射或 SQL 细节。
- 先调用模型的校验方法获取原始数据
- 检查返回值是否有效,无效则跳转错误页或返回 JSON 提示
- 确认通过后,再调用模型的 update 方法,传入 ID 和待更新字段(建议用 $this->db->where('id', $id)->update('table', $data))
- 更新后检查 $this->db->affected_rows() 是否为 1,确认真正改了数据
3. 使用事务保障原子性(关键场景必加)
当校验依赖多个表,或更新同时涉及关联表时,必须包裹事务。
- 在模型方法开头调用 $this->db->trans_start()
- 执行查询、更新等操作
- 结尾调用 $this->db->trans_complete(),并判断 $this->db->trans_status() 决定提交或回滚
- 注意:CI 的事务默认开启失败自动回滚,但需确保所有操作都在同一连接下(即不切换数据库组)
4. 避免常见陷阱
看似简单,实操中容易踩坑。
- 别在查询后手动拼 WHERE 条件更新:比如查出 $row['username'],再写 $this->db->where('username', $row['username'])->update(...) —— 若 username 可重复,可能误更多行
- 时间戳字段要显式处理:如果表有 updated_at,记得在 $data 数组里带上 'updated_at' => date('Y-m-d H:i:s'),除非启用了 CI 的自动时间戳功能
- POST 数据需过滤后再校验:比如 trim()、htmlspecialchars() 或使用 CI 的 $this->input->post(null, TRUE) 过滤 XSS











