ci4模型不能直接用构造函数参数注入服务,因服务容器默认不解析模型依赖;正确做法是调用parent::__construct()后通过config\services静态工厂获取服务,或注册为容器服务实现自动注入。

模型里不能直接用构造函数参数注入服务
CI4 的服务容器默认只对控制器、命令、过滤器等“入口类”做自动依赖解析,Model 类不在此列。你写 public function __construct(DatabaseInterface $db) 是无效的——框架不会帮你注入,会直接报错 ArgumentCountError 或空对象调用异常。
正确做法:在模型构造函数里手动获取服务
CI4 模型继承自 \CodeIgniter\Model,父类已初始化数据库连接,但其他服务(如缓存、邮件、日志)需显式获取。必须调用 parent::__construct() 保证基础功能可用,再通过 \Config\Services 静态工厂获取实例。
- 不要用
$this->load->library()—— 这是 CI3 语法,CI4 已废弃 - 避免在构造函数里执行耗时操作(如查询、文件读取),否则每次 new Model 都触发
- 若需复用逻辑,可封装成基类模型,在其构造函数中统一获取常用服务
示例:
namespace App\Models;
use CodeIgniter\Model;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Email\Email;
class UserModel extends Model
{
protected $cache;
public function __construct()
{
parent::__construct(); // 必须!否则 db 连接可能失效
$this->cache = \Config\Services::cache();
$this->email = \Config\Services::email();
}
public function getUserWithCache(int $id)
{
$key = 'user_' . $id;
$user = $this->cache->get($key);
if ($user === null) {
$user = $this->find($id);
$this->cache->save($key, $user, 300); // 缓存 5 分钟
}
return $user;
}
}
想用自动注入?得改注册方式
如果坚持要构造函数参数自动注入(比如测试时方便 mock),就得把模型注册进服务容器,而不是靠 new 实例化。
- 在
app/Config/Services.php中添加自定义服务定义 - 使用
singleton()或shared()注册模型类,声明其依赖 - 后续通过
\Config\Services::userModel()获取,而非new UserModel()
例如:
// app/Config/Services.php
public static function userModel($getShared = true)
{
if ($getShared) {
return static::getSharedInstance('userModel');
}
return new \App\Models\UserModel(
\Config\Services::database(),
\Config\Services::cache()
);
}
这样你在控制器里就能写 $this->userModel = \Config\Services::userModel();,且依赖由容器管理。
注意:模型里访问 session 会出问题
session() 是请求级服务,模型属于数据层,不该感知用户状态。强行在模型构造函数里调用 \Config\Services::session() 可能因生命周期错位导致 session 数据为空或报错 Session: Session not started。
真正需要用户上下文的逻辑(如记录操作人),应该由控制器或服务层传入 ID 或 token,而不是让模型自己去拿 session。
模型职责越单纯,越容易复用和测试;一旦混入请求上下文,就很难脱离 HTTP 环境运行 CLI 或单元测试。











