model 是唯一该碰数据库的地方,其他层必须通过明确接口调用model,helper和library不得直接查库;model需显式加载数据库,ci4推荐依赖注入与service协调多model操作。

Model 是唯一该碰数据库的地方,其他层调用它必须走明确接口,不能绕过。
为什么不能在 Helper 或 Library 里直接查数据库
CodeIgniter 的 Helper 是纯函数集合,没有 $this->db 上下文;Library 默认不自动加载 DB 类,硬写 $this->db->query() 会报 Call to a member function query() on null。即使手动加载,也破坏了职责边界——Helper 应只做格式化、字符串处理等无状态操作,Library 更适合封装跨 Model 的业务协调逻辑,而非数据访问本身。
Model 类必须继承 CI_Model 并显式加载 DB 类
CodeIgniter 3/4 中,Model 不是“自动拥有数据库”的魔法容器。你得主动声明依赖:
class User_model extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->database(); // 必须显式调用,尤其在多库场景下不可省略
}
public function get_by_id($id)
{
return $this->db->get_where('users', ['id' => $id])->row();
}
}
- 不写
$this->load->database()→$this->db为null,运行时报错 - 若项目用了多个数据库连接(如读写分离),需传参指定:
$this->load->database('slave', TRUE),返回独立实例 - CI4 中推荐用
\Config\Database::connect(),但 Model 仍需自己管理连接生命周期,不要在每次方法里重复 connect
自定义类想复用 Model 逻辑?用依赖注入,别硬耦合
比如你写了 Notification_service 类,需要发消息前查用户状态——别在 service 里 new 一个 User_model,也不要用 get_instance() 拿全局对象。正确方式是:让 Controller 把 Model 实例传进来。
// Controller 中 $user_model = new User_model(); $notification = new Notification_service($user_model); $notification->send($user_id);
- 硬 new Model 会跳过 CI 的自动加载机制,
$this->db不可用 -
get_instance()在非 Controller/Model 环境中可能返回空或异常,且导致测试困难 - 依赖注入让类职责清晰、可单元测试,也避免全局状态污染
CI4 中的例外:Service 类可替代传统 Library
CI4 官方鼓励用 Service 替代 Library,它天然支持 DI 和自动加载。你可以把数据库操作封装进 Service,但内部仍要通过 Model 或原生 Query Builder 完成:
class UserService
{
protected $userModel;
public function __construct(User_model $model)
{
$this->userModel = $model;
}
public function activate($id)
{
return $this->userModel->update_status($id, 'active'); // 调用 Model 方法,不直连 DB
}
}
- Service 本身不持有
$this->db,它只是协调者 - 所有 SQL 必须收口到 Model,哪怕只是简单
insert()或update() - 如果 Service 需要跨多个 Model 操作(如订单+库存+日志),Model 各自负责本域数据,Service 负责事务控制和错误回滚
Model 的事。其他地方出现 ->query() 或 ->table(),基本就是架构开始松动的信号。











