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.
Aliases are only picked up at table instantiation time
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
Apply connections dynamically in a more automated fashion
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
Cookbook > Events
Cookbook > Database Access & ORM > Behaviors
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.
Related
I need to change yii2 active database in controller.
What I have so far:
Yii::$app->db = new yii\db\Connection([
'dsn' => 'dblib:host='.$company->host.';port=1433;dbname=Interface',
'username' => $company->db_user,
'password' => $company->db_password,
]);
Yii::$app->db->open();
$users = User::find()->all();
Can I change active database like that and then start using models in new database and how?
You can define multiple databases in the config file with both databases referring to different objects. Use the object with the respective database you want to use.
For example:
If you have two databases defined in the config file db and db1.
Then you can just use db1 instead of db wherever you need.
I have an instanced web application that uses different databases for every instance but uses the same files.
What I want my application to do on boot:
Get database credentials from master instance table. This instance table is in the default 'mysql' connection that I have defined in app/config/database.php.
Connect to the instance database from that point on (I don't need the master instance database anymore)
I have tried to do this in my App:before() event, but that won't work as I don't have a sessions table in my master database. If I do it before the return array() in database.php it obviously can't connect because it doesn't have the correct master database credentials yet.
Where am I supposed to do this? Should I just disable sessions and do it in my App::before() and then enable sessions after?
Don't think its possible, But here is a possibility.
Read / Write Connections
Sometimes you may wish to use one database connection for SELECT statements, and another for INSERT, UPDATE, and DELETE statements. Laravel makes this a breeze, and the proper connections will always be used whether you are using raw queries, the query builder, or the Eloquent ORM.
To see how read / write connections should be configured, let's look at this example:
'mysql' => array(
'read' => array(
'host' => '192.168.1.1',
),
'write' => array(
'host' => '196.168.1.2'
),
'driver' => 'mysql',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
Note that two keys have been added to the configuration array: read and write. Both of these keys have array values containing a single key: host. The rest of the database options for the read and write connections will be merged from the main mysql array. So, we only need to place items in the read and write arrays if we wish to override the values in the main array. So, in this case, 192.168.1.1 will be used as the "read" connection, while 192.168.1.2 will be used as the "write" connection. The database credentials, prefix, character set, and all other options in the main mysql array will be shared across both connections.
After some hours of experimenting and trying I have found out that the best option really is to create a PDO instance manually in the /app/config/database.php file and get the database credentials that way:
// Get instance row
if (!App::runningInConsole() && !array_key_exists('instance_found', $_ENV)) {
$conn = new PDO('mysql:dbname=instances;host=127.0.0.1', 'root', 'password');
$stmt = $conn->prepare('SELECT * FROM instances WHERE http_host=?');
$stmt->bindParam(1, $_SERVER['HTTP_HOST']);
$stmt->execute();
$instance = $stmt->fetch(PDO::FETCH_OBJ);
$conn = null;
if (!$instance) {
return Response::make('Whoops!', 404);
} else {
$_ENV['instance_found'] = true;
$database = $instance->mysql_database;
$username = $instance->mysql_username;
$password = $instance->mysql_password;
}
}
I'm rather new to Laravel 4. What I have is a bunch of sports statistics information and for organization purposes I have a database for every sport (NFL, MLB, etc...). What I want to do is change the DB easily in queries and mimic the functionality of $myslqi->select_db() but aside from setting up a bunch of database connections in the config file, I can't find a way to do what I'm looking for. It's all the same connection and same user, I just want to be able to switch the DB without having to insert a variable into SQL in order to point to the right database.
You will need to define the connections in the config/database like so:
'nba' => array(
'driver' => 'mysql',
...
),
'nfl' => array(
'driver' => 'mysql',
...
),
And then use these in models schema or queries
class NbaPlayers extends Eloquent {
protected $connection = 'nba';
//other stuff on your model
}
$nflplayers = DB::connection('nfl')->whatever
If you define the connection in the model, you will use model::all() or whatever without having to define the connection everytime
I started develop in localhost, using yii 1.1.11 using the auto generate app, create users table, using gii to generate crud. When I migrate to webhosting, I have to use prefix for the table thus become "yii_users" and edit the config with this:
'db'=>array(
'connectionString' => 'xxxxx',
'username' => 'xxxxx',
'password' => 'xxxxx',
'tablePrefix' => 'yii_',
),
But I can no longer login and get error 'The table "users" for active record class "Users" cannot be found in the database. ' So I assume the tablePrefix doesn't work. How to fix this?
This requires you to change all your activerecords also, check the guide:
Info: To use the table prefix feature, the tableName() method for an AR class may be overridden ...
......
That is, instead of returning the fully qualified table name, we return the table name without the prefix and enclose it in double curly brackets
So you will need to do this:
public function tableName() {
return '{{user}}';
}
Also from tablePrefix doc:
By setting this property, any token like '{{tableName}}' ... will be replaced by 'prefixTableName', where 'prefix' refers to this property value.
(emphasis mine)
If so, how well? I can't seem to find any up to date information.
http://book.cakephp.org/view/1075/DataSources
DataSources are the link between models and the source of data that models represent. In many cases, the data is retrieved from a relational database such as MySQL, PostgreSQL or MSSQL. CakePHP is distributed with several database-specific datasources (see the dbo_* class files in cake/libs/model/datasources/dbo/), a summary of which is listed here for your convenience:
dbo_mssql.php
dbo_mysql.php
dbo_mysqli.php
dbo_oracle.php
dbo_postgres.php
dbo_sqlite.php
EDIT:
http://book.cakephp.org/view/922/Database-Configuration
Table below code listing.
driver row
The name of the database driver this configuration array is for. Examples: mysql, postgres, sqlite, pear-drivername, adodb-drivername, mssql, oracle, or odbc. Note that for non-database sources (e.g. LDAP, Twitter), leave this blank and use "datasource".
Have you just tried ti set the driver ?
var $default = array(
'driver' => 'sqlite',
'persistent' => false,
'host' => 'localhost',
'login' => 'login',
'password' => 'password',
'database' => 'full_path_to_sqlite_file',
'prefix' => ''
);
This might a duplicate of How do I connect CakePHP to a SQLite database?
Also have a look here for more information Using Sqlite3 with CakePHP
Digging through the CakePHP tickets it seems the developers won't add Sqlite3 support officially, but encourage the use of this plugin.
https://github.com/cakephp/datasources