Cakephp 3.0 ConnectionManager, getDataSource in Controller - cakephp-3.0

In CakePHP 3.0, what's the equivalent of calling getDataSource() from inside a controller (like $this->ModelName->getDataSource()in cakephp 2.x)?
I have tried this:
use Cake\Datasource\ConnectionManager;
$conn = ConnectionManager::get('my_connection');
Since this connection is connected already, why do I need to provide 'my_connection'?
How can I can get the DataSource from inside a Controller in CakePHP 3.0?
Thanks

Database\ConnectionManager::get() has been added. It replaces getDataSource()
CakePHP Cookbook 3

Begin, commit and rollback are now functions on the connection, not the data source. Use $conn = ConnectionManager::get($connectionName) and then use $conn->begin(), $conn->commit() and $conn->rollback().
http://book.cakephp.org/3.0/en/orm/database-basics.html#using-transactions
And use $this->Table->defaultConnectionName() to get the connection name to pass to get.

"my_connection" should be defined in cakephp 3 config
.go to cakephp/config/app.php
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
/**
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'root',
'password' => '',
'database' => 'cake',
'encoding' => 'utf8',
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
connection name is "default" for this case.
Each Model that used in controller have one Connection.
You can get connection in controller from model by this method.
$model=TableRegistry::get('table_name');
$connection = $model->connection();

Related

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')

CakePHP: default database is working, while test doesn't

Hi after changing to Schema type of database and the CakePHP version from 2.4.4 to 2.9.7 the test database just doesn't want to work: Error: Database connection "Mysql" is missing, or could not be created. when I want to run tests: ./Console/cake test app AllModel.
Here is my database.php file:
<?php class DATABASE_CONFIG {
public function __construct() {
$this->default = array(
'datasource' => 'Database/Mysql',
'driver' => 'mysql',
'persistent' => false,
'encoding' => 'utf8',
'prefix' => 'prefix_',
'host' => 'localhost',
'database' => 'db',
'login' => 'root', /*** replace this ***/
'password' => 'pass', /*** replace this ***/
);
$this->test = $this->default;
$this->test['database'] = $this->test['database'].'_test';
}
Here you can see, that the test database is just a copy of default one. But the default database is working and is connected, while the test database is giving me an error. What could it be ?
All I needed to do is to just create a database with a name db_test, but not populate it, because later it would be populated with fixtures.

Connect with multiple database in CakePHP 3

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
}

CakePHP connections while testing

i am currently trying to unit test some of my code. I am executing some manually entered SQL statements like this:
$db = ConnectionManager::get('default');
...
$stmt = $db->prepare($sql);
$stmt->execute();
I assumed that whenever I run unit tests and request the default connection I will get the test connection instead. If it is not defined in my config it will throw an exception.
When I run a test which executes a statement against the database it is always executed against the default connection. The test connection is never used.
Any idea what I am doing wrong?
My database config is as follows:
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'dbname',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
],
/**
* The test connection is used during the test suite.
*/
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'dbname_test',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
],
]
Thanks for any help!
Edit
I think the documentation is wrong:
By default CakePHP will alias each connection in your application. Each connection defined in your application’s bootstrap that does not start with test_ will have a test_ prefixed alias created. Aliasing connections ensures, you don’t accidentally use the wrong connection in test cases. Connection aliasing is transparent to the rest of your application. For example if you use the ‘default’ connection, instead you will get the test connection in test cases. If you use the ‘replica’ connection, the test suite will attempt to use ‘test_replica’.
Link
You can read there that CakePHP is aliasing the default connection automatically to test when running unit tests. To get that behaviour you have to define that alias yourself in your phpunit bootstrap.php like so:
\Cake\Datasource\ConnectionManager::alias('test', 'default');
You can read about that here ConnectionManager::alias The funny thing is that the documentation for alias explicitly states
For example, if you alias 'default' to 'test', fetching 'default' will always return the 'test' connection as long as the alias is defined.
We also had the same error, for some tables tests were getting default db instead of test db for no reason. In the end using code at the top of the setUp function (before parent::setUp()) solved the problem (we have a main test class from which we extend all tests, do some setting up. We put it there)
TableRegistry::clear();
The connection aliasing is done by default when you load the fixtures manager listener in our phpunit.xml config as it is distributed in the app template:
https://github.com/cakephp/app/blob/master/phpunit.xml.dist#L23-L31

CakePHP 2.1 doesn't work on localhost

I deployed my app on a remote host and everything works as expected. But when I try to test my code on localhost, it gives me the following error, without any change to the code working on the host:
Fatal error: Class 'AppHelper' not found in [path]
I am using CakePHP 2.1 and MySQL as my default datasource.
I connect to my local database just like to the remote one (with authentication changes):
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'database',
'prefix' => '',
'encoding' => 'utf8',
);
Why isn't this working on my localhost? Thank you
Two possible things:
either you didnt know about the AppHelper requirement for 2.1:
http://book.cakephp.org/2.0/en/appendices/2-1-migration-guide.html
or you forget to declare the helper at the very top of your class:
App::uses('AppHelper', 'View/Helper');
Although the second one is highly unlikely if you are not running any unit tests.
So my bet is on the first one.