Add setInstance() support for runtime database switching
PHP Version
8.3
CodeIgniter4 Version
4.6.1
CodeIgniter4 Installation Method
Composer (using codeigniter4/appstarter)
Which operating systems have you tested for this bug?
Windows
Which server did you use?
apache
Database
MySQL
What happened?
I would like to request a new method in Config\Database that allows developers to set or override a database connection instance at runtime without modifying the framework core.
This is especially useful when a project needs dynamic database switching, such as in:
Multi-tenant SaaS applications
ERP systems with client-specific databases
“Switch DB per request” API requirements
Dynamic “connect with custom credentials” scenarios
Currently, CodeIgniter 4 allows Database::connect($group) but there is no official method to replace or inject a database instance for a given group.
Proposed Method
Add this method in system/Database/Config.php:
public static function setInstance(string $group, array $config) { static::ensureFactory();
$connection = static::$factory->load($config, $group);
static::$instances[$group] = $connection;
return $connection;
}
Why this is needed
Today, Database::connect() always loads config from app/Config/Database.php.
To use a custom connection (hostname, username, password, database), we must create a fake group name or override config objects — both are hacky.
Dynamic DB switching is common in modern SaaS, ERP, and enterprise applications.
This addition would provide a clean, supported API for runtime DB instance control.
Example Use Case $dbConfig = [ 'DSN' => '', 'hostname' => $tenantHost, 'username' => $tenantUser, 'password' => $tenantPass, 'database' => $tenantDb, 'DBDriver' => 'MySQLi', 'DBPrefix' => '', ];
$connection = \Config\Database::setInstance('tenant', $dbConfig);
$db = \Config\Database::connect('tenant');
This gives developers the power to switch databases safely and officially.
Backward Compatibility
This method does not change any existing behavior.
It only adds a new optional utility method.
No BC breaks.
Conclusion
This feature will make CodeIgniter 4 significantly more flexible for advanced and enterprise applications that require dynamic DB switching.
Thank you for considering this enhancement! 🙏
Steps to Reproduce
I would like to request a new method in Config\Database that allows developers to set or override a database connection instance at runtime without modifying the framework core.
This is especially useful when a project needs dynamic database switching, such as in:
Multi-tenant SaaS applications
ERP systems with client-specific databases
“Switch DB per request” API requirements
Dynamic “connect with custom credentials” scenarios
Currently, CodeIgniter 4 allows Database::connect($group) but there is no official method to replace or inject a database instance for a given group.
Proposed Method
Add this method in system/Database/Config.php:
public static function setInstance(string $group, array $config) { static::ensureFactory();
$connection = static::$factory->load($config, $group);
static::$instances[$group] = $connection;
return $connection;
}
Why this is needed
Today, Database::connect() always loads config from app/Config/Database.php.
To use a custom connection (hostname, username, password, database), we must create a fake group name or override config objects — both are hacky.
Dynamic DB switching is common in modern SaaS, ERP, and enterprise applications.
This addition would provide a clean, supported API for runtime DB instance control.
Example Use Case $dbConfig = [ 'DSN' => '', 'hostname' => $tenantHost, 'username' => $tenantUser, 'password' => $tenantPass, 'database' => $tenantDb, 'DBDriver' => 'MySQLi', 'DBPrefix' => '', ];
$connection = \Config\Database::setInstance('tenant', $dbConfig);
$db = \Config\Database::connect('tenant');
This gives developers the power to switch databases safely and officially.
Backward Compatibility
This method does not change any existing behavior.
It only adds a new optional utility method.
No BC breaks.
Conclusion
This feature will make CodeIgniter 4 significantly more flexible for advanced and enterprise applications that require dynamic DB switching.
Thank you for considering this enhancement! 🙏
Expected Output
I would like to request a new method in Config\Database that allows developers to set or override a database connection instance at runtime without modifying the framework core.
This is especially useful when a project needs dynamic database switching, such as in:
Multi-tenant SaaS applications
ERP systems with client-specific databases
“Switch DB per request” API requirements
Dynamic “connect with custom credentials” scenarios
Currently, CodeIgniter 4 allows Database::connect($group) but there is no official method to replace or inject a database instance for a given group.
Proposed Method
Add this method in system/Database/Config.php:
public static function setInstance(string $group, array $config) { static::ensureFactory();
$connection = static::$factory->load($config, $group);
static::$instances[$group] = $connection;
return $connection;
}
Why this is needed
Today, Database::connect() always loads config from app/Config/Database.php.
To use a custom connection (hostname, username, password, database), we must create a fake group name or override config objects — both are hacky.
Dynamic DB switching is common in modern SaaS, ERP, and enterprise applications.
This addition would provide a clean, supported API for runtime DB instance control.
Example Use Case $dbConfig = [ 'DSN' => '', 'hostname' => $tenantHost, 'username' => $tenantUser, 'password' => $tenantPass, 'database' => $tenantDb, 'DBDriver' => 'MySQLi', 'DBPrefix' => '', ];
$connection = \Config\Database::setInstance('tenant', $dbConfig);
$db = \Config\Database::connect('tenant');
This gives developers the power to switch databases safely and officially.
Backward Compatibility
This method does not change any existing behavior.
It only adds a new optional utility method.
No BC breaks.
Conclusion
This feature will make CodeIgniter 4 significantly more flexible for advanced and enterprise applications that require dynamic DB switching.
Thank you for considering this enhancement! 🙏
Anything else?
No response
Among the several possible ways to connect to the database you can already specify custom config values when you create a connection, see Connecting with Custom Settings.
The group specifies the connection values already so adding custom values to completely replace all of the values in the configuration array doesn't make sense. I can see it making sense to allow setting override values when specifying the group name, since the most common use case that I can think of is a very large app with multiple databases of the same type. However, I don't know how common that is and if it needs to be in core since it's simple enough for an app handle that override with 2 lines of code, like:
$config = config('database.default');
$db = Database::connect([...config, 'database' => 'app2']);
I'd be interested to see what others have to say, though.
P.S. - maybe take the time to clean up your request - you seem to have multiple versions of the same request back to back....
I don't work with frequent DB switching. I don't understand why not use groups. It looks the same
I have never used a separate database connection per tenant, but are you sure you need to change the entire connection?
I would expect that tenants would share the same server, and just the database name would be changing. In that case, we already have setDatabase() method in the Connection class. Although I can imagine the need for a separate connection due to different credentials per tenant.
Honestly, since you have to load database credentials from somewhere - probably the master database? Then I would consider using Registrars to seed the tenant group array, which would be defined in the config (as an empty one). Then, in your code, you could just use the tenant connection group.
🔍 Clarification of My Use Case (Why setInstance() Is Needed)
Thank you for the feedback. Let me clarify my scenario, because my requirement cannot be solved by:
- Using predefined groups
- Using
Database::connect(configArray) - Using
setDatabase() - Using Registrars
My project is a Large-Scale Multi-Tenant SaaS CRM built in CodeIgniter 4 + HMVC with the following structure:
✔ 1. Each Application (Module) is a Separate Product
In my CRM, I can create multiple applications/products, and each application has:
- Its own controllers, models, and routes
- Its own migration files
- Its own DB group
- Its own module structure
Example:
Projects/BrillsenseCRM/City/CityModel.php
Projects/BrillsenseCRM/Country/CountryModel.php
Projects/BrillsenseCRM/Customer/CustomerModel.php
...etc
✔ 2. Each Application Has Multiple Clients (Multi-Tenant Per App)
Every client who buys a subscription for a particular application (project) gets their own separate database, not just a different schema name.
Example:
App1_Client_A_DB
App1_Client_B_DB
App1_Client_C_DB
App2_Client_A_DB
App2_Client_B_DB
➡️ Each client = separate database server OR database credentials.
✔ 3. DB Groups Cannot Be Predefined
I cannot define fixed groups in app/Config/Database.php because:
- I don’t know tenant DB credentials at development time
- Tenants can be created dynamically from CRM
- Tenants can update DB credentials later
- One application may have 500+ tenants
Defining 500 DB groups dynamically is not reasonable.
✔ 4. Why Current Solution Doesn't Work
❌ “Use Connect With Config Array”
Yes, I can do:
$config = config('database.default');
$db = Database::connect([...$config, 'database' => 'app2']);
But this does not register the connection inside Database::$instances[$group].
My entire project uses:
model('CityModel', false, 'App1TenantClientA');
When switching tenants, I need:
model('CityModel', false, 'tenantGroup')
⚠️ But group must exist in Database::$instances.
Without setInstance(), CI4 will still try to load from Config\Database.php.
❌ “Use setDatabase()”
This only changes the database name inside the same connection.
But in my case:
- hostname is different
- username is different
- password is different
- and sometimes DBDriver is different
So new connection is mandatory per tenant.
❌ “Use Registrars to seed groups”
Not possible because tenant DB details are generated:
- after subscription purchase
- can be modified
- can be deleted
- can be added dynamically anytime
I cannot generate groups on boot.
✔ 5. Why setInstance() Is Perfect for My Use Case
I want to do:
$dbConfig = [
'hostname' => $tenantHost,
'username' => $tenantUser,
'password' => $tenantPass,
'database' => $tenantDb,
'DBDriver' => 'MySQLi',
];
\Config\Database::setInstance('tenant_123', $dbConfig);
// Now models can naturally use the tenant instance
$db = \Config\Database::connect('tenant_123');
This allows:
- Clean DB switching
- No hacks
- No modifying framework core
- No defining 500+ groups manually
- Full compatibility with CI4 model system
- One-line switching of tenants
This mirrors patterns in other modern frameworks (Laravel, Symfony).
✔ 6. Summary of Why I Really Need This Feature
- I run a SaaS with unlimited applications
- Each application has unlimited clients
- Each client has a separate database
- Tenants are dynamic, not known at boot time
- I need to load runtime credentials
- Models depend on
$group - Dynamic group must be registered to
Database::$instances
Without setInstance(), I must hack into protected properties or override core classes — which I want to avoid.
🙏 Final Note
My goal is not to replace or conflict with existing features; I only want a clean, framework-supported way to inject database instances at runtime for multitenant SaaS systems.
Thank you for considering this enhancement. I believe many enterprise-level CI4 applications will benefit from this small but powerful feature.
Yes, I can do:
$config = config('database.default'); $db = Database::connect([...$config, 'database' => 'app2']);But this does not register the connection inside
Database::$instances[$group].
That is not true. The entry in the database instances is always created. It just has a different naming convention.
I want to do:
$dbConfig = [ 'hostname' => $tenantHost, 'username' => $tenantUser, 'password' => $tenantPass, 'database' => $tenantDb, 'DBDriver' => 'MySQLi', ]; \Config\Database::setInstance('tenant_123', $dbConfig); // Now models can naturally use the tenant instance $db = \Config\Database::connect('tenant_123');
The main problem I have with this approach is that it will not work as expected when we call for a "none shared" instance.