Reputation: 3
In the controller I want to change my default database connection, such that I can access a different database. It used to work, I created a function in the AppController, calling it in each controller when needed, containing the following:
ConnectionManager::config('database', [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => $username,
'password' => $password,
'database' => $database,
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
]);
ConnectionManager::alias('database', 'default');
I created a new database connection, calling it 'database', and aliased default to this connection, such that instead of the default connection, this connection will be used. However, when I print
$this->{$modelName}->connection()->config();
It still gives the default connection. And an error saying the table doesn't exist, confirms I am not in the new connection's database.
Upvotes: 0
Views: 2036
Reputation: 349
I built a sample project with dynamic connections based on the session variable. I hope somebody find it useful: https://github.com/jszoja/cakephp3-multidb
I documented it there.
Upvotes: 1
Reputation: 60463
Alising connections on the fly generally works fine, but models / table classes only pick up connections up on their own once when they are being instantiated.
So if it doesn't work for a specific table, this is most probably because it has already been instantiated and picked up the default connection (the table registry only instantiates a table class once per alias).
So, either make sure that the tables are being instanted afterwards, which may require clearing (TableRegistry::clear()
) / removing from (TableRegistry::remove()
) the registry if the alias needs to be created late in the request cycle (which however could cause other problems, as you'd loose possibly applied dynamic configuration), or set the connection on the involved table(s) manually:
\Cake\Datasource\ConnectionManager::alias('database', 'default');
$connection = \Cake\Datasource\ConnectionManager::get('default');
$this->{$modelName}->setConnection($connection); // connection() before CakePHP 3.4
If you need this on multiple, or maybe all tables, or if you just want to shift away the responsibility from the controller (it most probably shouldn't be overly involved in configuring the model layer), then maybe dispatch an event when creating the alias, and use a base table class or a behavior that listens to the event, or maybe even use a "service" that knows about the tables and updates the tables connections accordingly.
\Cake\Datasource\ConnectionManager::alias('database', 'default');
$connection = \Cake\Datasource\ConnectionManager::get('default');
$event = new \Cake\Event\Event('Connection.aliased', $connection, ['source' => 'default']);
\Cake\Event\EventManager::instance()->dispatch($event);
For example in a table you could then do something like this, applying the new connection in case the table is configured to use the connection that has been aliased, ie if defaultConnectionName() === 'default'
, then pick up the new connection:
\Cake\Event\EventManager::instance()->on(
'Connection.aliased',
function (\Cake\Event\Event $event) {
// data() before CakePHP 3.4
if ($event->getData('source') === static::defaultConnectionName()) {
$this->setConnection($event->getSubject()); // subject() before CakePHP 3.4
}
}
);
See also
Upvotes: 2