You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CodeIgniter HMVC中基于子域名实现多租户多数据库的SaaS架构

我之前在CodeIgniter HMVC架构下做过类似的多租户SaaS项目,给你梳理一套可行的实现流程,分步骤来操作:

1. 本地开发:子域名Hosts配置

首先得让本地能访问这些子域名,需要修改系统Hosts文件:

  • Linux/Mac 用户打开 /etc/hosts,Windows 用户打开 C:\Windows\System32\drivers\etc\hosts
  • 添加以下三条记录:
    127.0.0.1 www.client_1.localhost.com
    127.0.0.1 www.client_2.localhost.com
    127.0.0.1 www.client_3.localhost.com
    

保存后,本地访问这些子域名就能指向你的CI项目了。

2. 数据库配置文件改造

在 application/config/database.php 里,除了默认的数据库配置,新增每个客户端的专属数据库配置:

// 默认 fallback 数据库(子域名识别失败时使用)
$db['default'] = array(
    'dsn'   => '',
    'hostname' => 'localhost',
    'username' => 'root',
    'password' => 'your_password',
    'database' => 'saas_fallback',
    'dbdriver' => 'mysqli',
    // 其他默认配置(如dbprefix、pconnect等)
);

// 客户端1数据库配置
$db['client_1'] = array(
    'hostname' => 'localhost',
    'username' => 'client1_user',
    'password' => 'client1_pass',
    'database' => 'client_1_db',
    'dbdriver' => 'mysqli',
    // 同步其他默认配置
);

// 客户端2数据库配置
$db['client_2'] = array(
    'hostname' => 'localhost',
    'username' => 'client2_user',
    'password' => 'client2_pass',
    'database' => 'client_2_db',
    'dbdriver' => 'mysqli',
);

// 客户端3数据库配置
$db['client_3'] = array(
    'hostname' => 'localhost',
    'username' => 'client3_user',
    'password' => 'client3_pass',
    'database' => 'client_3_db',
    'dbdriver' => 'mysqli',
);

注意:生产环境建议给每个客户端分配独立的数据库账号,只授予对应数据库的读写权限,避免权限过大带来的风险。

3. 核心逻辑:子域名解析+自动切换数据库

这里推荐两种方案,选适合你项目的就行:

方案一:使用CodeIgniter钩子(推荐)

钩子能在请求生命周期的特定阶段执行代码,适合全局统一处理:

  1. 先打开 application/config/config.php,开启钩子:
    $config['enable_hooks'] = TRUE;
    
  2. 在 application/config/hooks.php 中注册一个post_controller_constructor钩子:
    $hook['post_controller_constructor'] = array(
        'class'    => 'DbSwitcher',
        'function' => 'switchClientDatabase',
        'filename' => 'DbSwitcher.php',
        'filepath' => 'hooks',
        'params'   => array()
    );
    
  3. 创建 application/hooks/DbSwitcher.php 文件,编写切换逻辑:
    class DbSwitcher {
        public function switchClientDatabase() {
            $CI =& get_instance();
            // 获取当前访问的域名
            $currentHost = $_SERVER['HTTP_HOST'];
            // 解析子域名:从www.client_1.localhost.com中提取client_1
            $hostParts = explode('.', $currentHost);
            // 这里根据你的域名结构调整索引,比如hostParts[1]就是客户端标识
            $clientKey = $hostParts[1];
    
            // 检查是否存在对应客户端的数据库配置
            if (isset($CI->db->database[$clientKey])) {
                // 切换到客户端专属数据库
                $CI->db = $CI->load->database($clientKey, TRUE);
            } else {
                // 切换到默认fallback数据库,也可以抛出404错误
                $CI->db = $CI->load->database('default', TRUE);
            }
        }
    }
    

方案二:HMVC核心控制器继承

如果你的项目所有模块控制器都继承自自定义的核心控制器(比如MY_Controller),可以把切换逻辑写在核心控制器的构造函数里:

class MY_Controller extends CI_Controller {
    public function __construct() {
        parent::__construct();
        // 初始化时自动切换数据库
        $this->switchClientDb();
    }

    private function switchClientDb() {
        $currentHost = $_SERVER['HTTP_HOST'];
        $hostParts = explode('.', $currentHost);
        $clientKey = $hostParts[1];

        if (isset($this->db->database[$clientKey])) {
            $this->db = $this->load->database($clientKey, TRUE);
        } else {
            $this->db = $this->load->database('default', TRUE);
        }
    }
}

这样所有继承MY_Controller的HMVC模块控制器,都会自动使用当前客户端的数据库连接。

4. HMVC模块中的数据库使用

模块里的控制器只要继承了MY_Controller(或者通过钩子全局切换后),直接用$this->db就能操作当前客户端的数据库,和普通CI项目的用法完全一致,不需要额外修改模块代码,保证了代码库的统一性。

5. 生产环境进阶优化

如果后续要新增大量客户端,硬编码数据库配置会很麻烦,建议做以下优化:

  1. 新建一个主数据库(比如saas_master_db),专门存储客户端信息(子域名、数据库配置、租户信息等)
  2. 在切换数据库时,先连接主数据库,根据子域名查询对应客户端的数据库配置,再动态加载:
    public function switchClientDatabase() {
        $CI =& get_instance();
        // 先连接主数据库
        $masterDb = $CI->load->database('master', TRUE);
        // 获取当前子域名
        $currentHost = $_SERVER['HTTP_HOST'];
        $hostParts = explode('.', $currentHost);
        $subdomain = $hostParts[1];
        // 查询客户端数据库配置
        $clientInfo = $masterDb->get_where('tenants', ['subdomain' => $subdomain])->row();
        
        if ($clientInfo) {
            // 动态构建数据库配置数组
            $clientDbConfig = array(
                'hostname' => $clientInfo->db_host,
                'username' => $clientInfo->db_user,
                'password' => $clientInfo->db_pass,
                'database' => $clientInfo->db_name,
                'dbdriver' => 'mysqli'
            );
            // 加载客户端数据库
            $CI->db = $CI->load->database($clientDbConfig, TRUE);
        } else {
            $CI->db = $CI->load->database('default', TRUE);
        }
    }
    

这样新增客户端时,只需要在主数据库添加一条记录,不需要修改代码或配置文件,扩展性更强。

内容的提问来源于stack exchange,提问作者user7485162

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:57:10