Connect with multiple database in CakePHP 3 - mysql

I want to store some data histories and logs in other database table it may be very large database in terms of records in future. Also cross database SQL joing should be supported. So please help me for solution for this.
Thanks in advance

You have follow below steps fo use multiple datasource within same cakephp application.
Mention multiple database source in Config/app.php
You have to manage multiple data source configurations, in default data source keep main database, and make one more data source we can say it history for second data source. As mentioned below
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => '<host name>',
'username' => '<database user>',
'password' => '<database password>',
'database' => '<database name>',
'encoding' => 'utf8',
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => false,
'url' => env('DATABASE_URL', null),
],
'history' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => '<host name>',
'username' => '<database user>',
'password' => '<database password>',
'database' => '<database name>',
'encoding' => 'utf8',
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => false,
'url' => env('DATABASE_URL', null),
]
]
Specify data source in Tables Classes
In Src/Model/Table/<AnyOtherSource>Table.php, Add below method, where you want to use history data source. No need to add below method where you need to use default data source, for default data source CakePHP will take care of it.
public static function defaultConnectionName() {
return 'history';
}
Joining and Model association in CakePHP 3
You access other databases on the same MySQL instance by prefixing the
table with the appropriate database name. You have to specify table
name with database_name.table_name, For this you need to add below
code in each table classes.
For Example,
SELECT * FROM this_database.table_1 t1 JOIN that_database.table_2 t2 ON t2.column = t1.column
Add below code in Src/Model/Table/<All>Table.php
class LogsTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->table($this->connection()->config()['database'] . "." . $this->table()); // this is very important for joining and associations.
// your other code for initilize method
}
public static function defaultConnectionName() {
return 'history';
}
// other methods and your code should be here
}

Related

Laravel using multiple databases (2 MySQL, 1 SQLite) [duplicate]

This question already has answers here:
How to use multiple databases in Laravel
(7 answers)
Closed last month.
I'm developing a project where I need to use multiple databases in Laravel. The first database works like a charm (MySQL) but I can't get the framework to recognize the SQLite DB. The error that shows up is this:
Illuminate \ Database \ QueryException
Database (sqlite) does not exist. (SQL: PRAGMA foreign_keys = ON;)
Here is my .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sitio1
DB_USERNAME=root
DB_PASSWORD=
DB_CONNECTION_SECOND=sqlite
DB_DATABASE_SECOND=/Users/imac/CleverOctopusBDD/database/Sitio2.sqlite
DB_USERNAME_SECOND=root
DB_PASSWORD_SECOND=
and here is my database.php
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_CONNECTION_SECOND', database_path('Sitio2.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'sitiocentral'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
]) : [],
],
And the location of my .sqlite file is this -> database -> Sitio2.sqlite
I've doing some research and I don't really know what am I doing wrong. If you are wondering, this is how one of my sqlite db model looks...
class Client2 extends Model
{
protected $connection = 'sqlite';
protected $fillable = [
'name', 'email', 'phone', 'address'
];
protected $table = 'Client2';
protected $primaryKey = 'idClient';
public $timestamps = false;
}
I hope you can help me out here because I have found information about multiple MySQL connections but not with SQLite, I appreciate any tips given.
EDIT: I just needed to pass the full path to 'database' => env('DB_DATABASE_SECOND', database_path('Sitio2.sqlite')),
does it work if you put the full path to your sqlite file in the database config like this?
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => '/Users/imac/CleverOctopusBDD/database/Sitio2.sqlite',
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],

cakephp + phpunit + gitlab continuous integration

I'm looking for help with my tests in cakephp + phpunit + gitlab-ci.
I defined my test/fixtures like this: public $import = ['model' => 'MyModel']; to avoid having to redefine the tables schema in the fixture, and have out of sync/wrong code.
I understand that what it does is to look for the live db and use that information to generate the tables in the test db, but what about if there is no live db?
I'm trying to automate my testing in gitlab / continuous integration environment, and there they provide you a mysql container with a single db to run your tests. If I have a single DB, i will not be able to have both a main connection and the testing db, right?
I was thinking on having a create-db-for-tests.sql and imprort that script BUT I understand that running a test with a fixture will remove the table, so I would need to create it again and again on every test.
I could not use the gitlab mysql service and install mysql in the testing container to have 2 databases, like in dev, but I would need to populate the other DB with some script too, and that's the same as defining the db structure in the fixtures.
How can I solve this problem?
Any help will be gladly appreciated.
Something came out from my mind after I read your question.
If you don't have live db, there might be two possibility which is
you may have staging db or your application don't have DB.
According to your second question related about If I have single DB, I would say No. Because, you could have two connections.
e.g
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => 'root',
'database' => 'foge',
'prefix' => '',
'encoding' => 'utf8mb4',
);
public $test = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => 'root',
'database' => 'test_foge',
'prefix' => '',
'encoding' => 'utf8mb4',
);
When creating a fixture you will mainly define two things: how the table is created (which fields are part of the table), and which records will be initially populated to the table.
I think, you can populate your script with this. Because this is mysql command. But, say honestly i haven't try it before.
mysql -u jenkins -pcakephp_jenkins -e 'DROP DATABASE IF EXISTS test_foge; CREATE DATABASE test_foge; CREATE TABLE users; CREATE Table posts';
And the last one is you can populate data before you run it and I guess you already knew it.
class ArticleFixture extends CakeTestFixture {
public $fields = array(
'id' => array('type' => 'integer', 'key' => 'primary'),
'title' => array('type' => 'string', 'length' => 255, 'null' => false),
'body' => 'text',
'published' => array('type' => 'integer', 'default' => '0', 'null' => false),
'created' => 'datetime',
'updated' => 'datetime'
);
public function init() {
$this->records = array(
array(
'id' => 1,
'title' => 'First Article',
'body' => 'First Article Body',
'published' => '1',
'created' => date('Y-m-d H:i:s'),
'updated' => date('Y-m-d H:i:s'),
),
);
parent::init();
}
}
As my personal perspective, good idea to drop and re-create the database before each build as well. This insulates you from chained failures, where one broken build causes others to fail.

Adding multiple connections in Laravel [duplicate]

I want to combine multiple databases in my system. Most of the time the database is MySQL; but it may differ in future i.e. Admin can generate such a reports which is use source of heterogeneous database system.
So my question is does Laravel provide any Facade to deal with such situations? Or any other framework have more suitable capabilities for problem is?
Tested versions (Updated)
Version
Tested (Yes/No)
4.2
No
5
Yes (5.5)
6
No
7
No
8
Yes (8.4)
9
Yes (9.2)
Define Connections
Using .env >= 5.0 (or higher)
In .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mysql_database
DB_USERNAME=root
DB_PASSWORD=secret
DB_CONNECTION_PGSQL=pgsql
DB_HOST_PGSQL=127.0.0.1
DB_PORT_PGSQL=5432
DB_DATABASE_PGSQL=pgsql_database
DB_USERNAME_PGSQL=root
DB_PASSWORD_PGSQL=secret
In config/database.php
'mysql' => [
'driver' => env('DB_CONNECTION'),
'host' => env('DB_HOST'),
'port' => env('DB_PORT'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
],
'pgsql' => [
'driver' => env('DB_CONNECTION_PGSQL'),
'host' => env('DB_HOST_PGSQL'),
'port' => env('DB_PORT_PGSQL'),
'database' => env('DB_DATABASE_PGSQL'),
'username' => env('DB_USERNAME_PGSQL'),
'password' => env('DB_PASSWORD_PGSQL'),
],
Note: In pgsql, if DB_username and DB_password are the same, then you can use env('DB_USERNAME'), which is mentioned in .env first few lines.
Without .env <= 4.0 (or lower)
app/config/database.php
return array(
'default' => 'mysql',
'connections' => array(
# Primary/Default database connection
'mysql' => array(
'driver' => 'mysql',
'host' => '127.0.0.1',
'database' => 'mysql_database',
'username' => 'root',
'password' => 'secret'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
# Secondary database connection
'pgsql' => [
'driver' => 'pgsql',
'host' => 'localhost',
'port' => '5432',
'database' => 'pgsql_database',
'username' => 'root',
'password' => 'secret',
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
]
),
);
Schema / Migration
Run the connection() method to specify which connection to use.
Schema::connection('pgsql')->create('some_table', function($table)
{
$table->increments('id'):
});
Or, at the top, define a connection.
protected $connection = 'pgsql';
Query Builder
$users = DB::connection('pgsql')->select(...);
Model (In Laravel >= 5.0 (or higher))
Set the $connection variable in your model
class ModelName extends Model { // extend changed
protected $connection = 'pgsql';
}
Eloquent (In Laravel <= 4.0 (or lower))
Set the $connection variable in your model
class SomeModel extends Eloquent {
protected $connection = 'pgsql';
}
Transaction Mode
DB::transaction(function () {
DB::connection('mysql')->table('users')->update(['name' => 'John']);
DB::connection('pgsql')->table('orders')->update(['status' => 'shipped']);
});
or
DB::connection('mysql')->beginTransaction();
try {
DB::connection('mysql')->table('users')->update(['name' => 'John']);
DB::connection('pgsql')->beginTransaction();
DB::connection('pgsql')->table('orders')->update(['status' => 'shipped']);
DB::connection('pgsql')->commit();
DB::connection('mysql')->commit();
} catch (\Exception $e) {
DB::connection('mysql')->rollBack();
DB::connection('pgsql')->rollBack();
throw $e;
}
You can also define the connection at runtime via the setConnection method or the on static method:
class SomeController extends BaseController {
public function someMethod()
{
$someModel = new SomeModel;
$someModel->setConnection('pgsql'); // non-static method
$something = $someModel->find(1);
$something = SomeModel::on('pgsql')->find(1); // static method
return $something;
}
}
Note: Be careful about building relationships with tables across databases! It is possible to do, but it can come with caveats depending on your database and settings.
From Laravel Docs
Using Multiple Database Connections
You may access each connection via the connection method on the DB facade when using multiple connections. The name passed to the connection method should correspond to one of the connections listed in your config/database.php configuration file:
$users = DB::connection('foo')->select(...);
You may also access the raw, underlying PDO instance using the getPdo method on a connection instance:
$pdo = DB::connection()->getPdo();
Useful Links
Laravel 5 multiple database connections FROM laracasts.com
Connect multiple databases in Laravel FROM tutsnare.com
Multiple DB Connections in Laravel FROM fideloper.com
In Laravel 5.1, you specify the connection:
$users = DB::connection('foo')->select(...);
Default, Laravel uses the default connection. It is simple, isn't it?
Read more here: http://laravel.com/docs/5.1/database#accessing-connections
Actually, DB::connection('name')->select(..) doesnt work for me, because 'name' has to be in double quotes: "name"
Still, the select query is executed on my default connection. Still trying to figure out, how to convince Laravel to work the way it is intended: change the connection.
Edit: I figured it out. After debugging Laravels DatabaseManager it turned out my database.php (config file) (inside $this->app) was wrong. In the section "connections" I had stuff like "database" with values of the one i copied it from. In clear terms, instead of
env('DB_DATABASE', 'name')
I needed to place something like
'myNewName'
since all connections were listed with the same values for the database, username, password, etc. which of course makes little sense if I want to access at least another database name
Therefore, every time I wanted to select something from another database I always ended up in my default database
Laravel has inbuilt support for multiple database systems, you need to provide connection details in config/database.php file
return [
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
'mysqlOne' => [
'driver' => 'mysql',
'host' => env('DB_HOST_ONE', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_ONE', 'forge'),
'username' => env('DB_USERNAME_ONE', 'forge'),
'password' => env('DB_PASSWORD_ONE', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
];
Once you have this you can create two base model class for each connection and define the connection name in those models
//BaseModel.php
protected $connection = 'mysql';
//BaseModelOne.php
protected $connection = 'mysqlOne';
You can extend these models to create more models for tables in each DB.
Also you can use postgres fdw system
https://www.postgresql.org/docs/9.5/postgres-fdw.html
You will be able to connect different db in postgres. After that, in one query, you can access tables that are in different databases.
This worked for me
The Middleware:
<?php
namespace App\Http\Middleware;
use Config;
use Closure;
use DB;
class DBSelect
{
public function handle($request, Closure $next)
{
//$db_name = "db1";
$db_name = "db2";
Config::set('database.connections.mysql.database', $db_name);
DB::reconnect('mysql');
return $next($request);
}
}
global Kernel.php
protected $middleware = [
.....
\App\Http\Middleware\DBSelect::class,
];
I changed some code from this answer (https://stackoverflow.com/a/64744187/4514022) and it worked for me.
Not a good solution if you want to clone the existing system and to run the existing code on a new database for a new customer.
We would have to edit hundreds of eloquent calls to insert the DB::connection('foo')

How to Get values for DB configuration from some database Tables in CakePHP ?

I am making an application that will be installed at multiple clients , and DB configuration for every client will be different , as i will be using multiple different databases LIKE Oracle and MySql.
One database will be common in all , i have made a table in same where i will save the db config details , now how to pick that data from the table at database.php .
Can't find anything bit confused .`class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => 'root',
'database' => 'hrportal_imp',
'prefix' => '',
//'encoding' => 'utf8',
);
//want to fetch data from x table from Default datasource.
public $ora = array(
'datasource' => 'Database/Oracle',
'persistent' => false,
'host' => '<IP i get from above db>',
'port' => '1521',
'login' => '<Data i get from above db>',
'password' => '<Data i get from above db>',
'database' => '<IP i get from above db>:1521/orcl',
'prefix' => '',
'sid' => 'orcl'
);
}`
I did it by Placing the same in Beforefilter of AppController .
App::import('Model', 'ConnectionManager');
ConnectionManager::create('ora',
$config = array('datasource' => 'Database/Oracle',
'persistent' => false,
'host' => 'dynamic Host',
'port' => '1521',
'login' => 'HCM',
'password' => 'hdhd',
'database' => 'dynamic host:1521/dhdh',
'prefix' => '',
'sid' => 'orcl')
);
by default, cakephp will use "default" configurations that you can write within your $default variable. you can also change your database connection as per requirements too. you can check your current selected database on your controller too.
App::import('Model', 'ConnectionManager');
$ds = ConnectionManager::getDataSource('default');
echo $ds->config['database'];

How to connect MongoDB and MySQL in same project(app) in Yii/Yii2?

I have a requirement to use two types of databases in single project/app. Now I am confused how to do that. I tried individual and they worked but I don't know how to use it.
For Yii 2 you can set two db components in configuration like:
'components' => [
'dbMySQL' => [
'class' => '\yii\db\Connection',
'dsn' => 'mysql:...',
'username' => '...',
'password' => '...',
'charset' => 'utf8',
],
'dbMongo' => [
'class' => '\yii\mongodb\Connection',
'dsn' => 'mongodb://...',
],
],
and then use it where you want
Yii::$app->dbMySQL->...
Yii::$app->dbMongo->...
Remember to override getDb() method in \yii\db\ActiveRecord and \yii\mongodb\ActiveRecord classes to point to these components if you use ActiveRecords.