A question for the CakePHP community:
I am attempting to upgrade a website built using CakePHP 1.3.11 to CakePHP 2.0.4. It is a very simple site with no database attached, I wish to use the PagesController / PageModel to validate the data passed to it from a Form and then send an E-Mail based on the information supplied.
The $useTable variable in PageModel has been set to false and the database.php $default is left as.. well, default (the reasoning being i dont need a database conneciton at anytime).
The problem comes in, when I try loading the view, i get:
Error: SQLSTATE[28000] [1045] Access denied for user 'user'#'localhost' (using password: YES) requires a database connection.
Why would it give me this when at no time do i want a call to any database? Worked fine in 1.3.11, am I missing something...? Hope I explained the dilemma adequately. Any input would be much appreciated.
It's slightly different for Cake 2.0 I believe; I've used this on a similar project:
In:
APP/Model/Datasource/Database/fake.php // fake.php is your class name
<?php
App::uses('DboSource', 'Model/Datasource');
// allows Cake to function without a database connection
class Fake extends DboSource {
public $description = "Disable the requirement of connecting to a database.";
function connect()
{
$this->connected = true;
return $this->connected;
}
function disconnect() // probably not necessary but for completion
{
$this->connected = false;
return !$this->connected;
}
}
and in database.php
class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/Fake', // usually Database/Mysql
'persistent' => false,
'host' => 'localhost',
'login' => 'user',
'password' => '',
'database' => '',
'prefix' => '',
'encoding' => 'utf8',
);
// I left the other keys in in case you need to swap
// to a real connection quickly at some point.
this works in 2.0.4
It seems that you need to create a dummy datasource if you are not using any database.
I have never done this, but this thread looks promising: http://groups.google.com/group/cake-php/browse_thread/thread/1254d64d4b21b24c
Related
This is a similar situation:
Multi tenancy in Laravel Eloquent ORM
I would like to modify eloquent model to have a variable database connection name. I have the name available as a string. I will have hundreds of models connecting to a tenant database, so I want a one-liner...
I have tried several approaches. The one that works is this:
$posUser = new posUser();
$posUser->setConnection($this->system->getDBC());
$posUser->create($posUserData);
with the classes set up like this:
class posUser extends myModel
{
}
class myModel extends Model
{
public function setConnection($dbc)
{
$this->connection = $dbc;
}
}
However this is a bit verbose.
What I want is a one liner:
posUser::create($posUserData);
Which, I do have this on liner working by creating a new database connection and setting that connection to the default, which happens when the registered user visits the site. I would feel more comfortable with a command like (which errors...)
posUser::On($connection_name)->create($posUserData);
And require all models using the tenant databases to specify the connection. I am simply terrified that one tenant will somehow write to another tenant database. Any ideas?
The Answer for me so far is to set a connection as the default, then operate as normal.
1) get the system connection information from a database using a "main" connection
2) create the tenant connection
3) set it to default
Then all the tenant models/queries you will not supply a connection to. The main connection for me is set as 'main'.
Tenant Connection class
class TenantDatabaseConnector
{
public static function GetDBCPrefix()
{
return strtolower(env('DB_PREFIX')) .'_'.env('MAIN_DB_NAME') . '_';
}
public static function createTenantConnection(System $system)
{
if(! self::checkDB($system->dbc()))
{
dd('You sure there is a database named ' . $system->dbc());
}
$env = strtoupper(env('DB_PREFIX'));
$connections = Config::get('database.connections');
$tenant_connection = [
'driver' => 'mysql',
'host' => env($env . '_DB_HOST'),
'database' => $system->dbc(),
'username' => env($env. '_DB_USERNAME'),
'password' => env($env .'_DB_PASSWORD'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'port' => '3306'
];
$connections[$system->dbc()] = $tenant_connection;
Config::set('database.connections', $connections);
self::setDefaultDBC($system);
return true;
}
public static function setDefaultDBC($system)
{
Config::set('database.default', $system->dbc());
}
public static function checkDB($dbc)
{
$sql = "SELECT SCHEMA_NAME FROM
INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = '". $dbc ."'";
$dbexists = DB::connection('main')->select($sql);
if(sizeof($dbexists) > 0)
{
return true;
}
return false;
}
}
You see a $env variable which I am using to set local_, alpha_, beta_, testing_ prefixes to my databases.
I working on a project where user register and get a free online shop using CakePHP v2.6.5. I'm using shared hosting so i can't create Mysql database for them, So I use sqlite instead. Because I need to use it with Mobile App too. In my code I dynamically create sqlite file for user after their register. But I don't know how to change database path in CakePHP. All I know is CakePHP use config in config/database.php file, but i can't init it dynamically.
In my config/database.php file:
public $sqliteDB = array(
'datasource' => 'Database/Sqlite',
'persistent' => false,
//'database'=>'',
'prefix' => '',
'encoding' => 'utf8'
);
I trying to follow these link:
CakePHP switch database (using same datasource) on the fly?
and this link
Cakephp can't change database on-the-fly, but I got nothing.
I figure out how to do it by create ConnectionManager on the fly in __construct() method of model. But I use it with multi table so here my code
In AppModel:
public function connectSqlite()
{
App::uses('CakeSession', 'Model/Datasource');
$shop = CakeSession::read('SHOP'); //just read Session for shop's name, because i create a directory and store sqlite in it. Directory name base on shop name that user defined when user registerd.
if($shop)
{
$dbName=FILES_URL.'shops'.DS.$shop['name'].DS.'database'.DS.'database.sqlite'; //Sqlite file path
$sqliteDB = array(
// Set correct database name
'datasource' => 'Database/Sqlite',
'persistent' => false,
'database'=>$dbName,
'prefix' => '',
'encoding' => 'utf8'
);
// Add new config to registry
ConnectionManager::create($dbName, $sqliteDB); //create ConnectionManager and return it to derived class.
// Point model to new config
return $dbName;
}
}
In Product Model:
public function __construct($id = false, $table = null, $ds = null)
{
$dbName=parent::connectSqlite();
if($dbName){
$this->useDbConfig=$dbName;
}
parent::__construct($id, $table, $ds);
}
Hope this can help!
We have a huge app built on Yii.
So far migrating was done by copy-pasting SQL dumps in phpliteadmin/phpmyadmin, but that's not very convenient.
I want to use CDbMigrations for it, with yiic migrate.
The issue is that we have two databases - a small sqlite one for config and settings, and large mysql for storing some historical records.
How do I specify what database the migration belongs to?
This is the basic migration structure, for illustration.
class do_stuff extends CDbMigration
{
public function up()
{
// create table
}
public function down()
{
// drop table
}
}
In your config.php file, specify your database connections in the components array.
'db1'=>array(
'class'=>'CDbConnection',
'connectionString' => 'mysql:charset=utf8mb4;host=localhost;dbname=dbname1',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
),
'db2'=>array(
'class'=>'CDbConnection',
'connectionString' => 'mysql:charset=utf8mb4;host=localhost;dbname=dbname2',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
),
Then in your migration, you can do the following:
protected $dbConnection1;
protected function getDbConnection1()
{
if (null !== $this->dbConnection1) {
return $this->dbConnection1;
}
return $this->dbConnection1 = Yii::app()->getComponent('db1');
}
Repeat for your second connection.
Then use those methods to get the CDbConnection for each database, from which you can create commands to execute.
You can use CDbMigration::setDbConnection() to change the default, or override the getDbConnection() method. This is similar to another proposed solution, but it had a typo and it needed to be a public function, so I include here the fix.
In your migration, include the following (change db1 for the defined connection name in your configuration files):
protected $dbConnection1;
public function getDbConnection()
{
if (null !== $this->dbConnection1) {
return $this->dbConnection1;
}
return $this->dbConnection1 = Yii::app()->getComponent('db1');
}
I want to switch DB based on domain, selecting credentials from another DB, but I can't switch..
AppController.php
// Select username, password and database based on domain
$this->Company->find('first', [...]);
if ($company) {
// Connect to second database, droping connection from first.
$dataSource = ConnectionManager::getDataSource('default');
$dataSource->config['login'] = $company['Company']['dbuser'];
$dataSource->config['password'] = $company['Company']['dbpass'];
$dataSource->config['database'] = $company['Company']['dbname'];
/**
* PROBLEM START HERE:
* Here, need to use new database settings, and, this case
* Company table does not exists, but I always get it, so,
* I think I am connected with the first and not second connection.
*/
print_r($this->Company->find('first'));
}
How I can correct this?
EDIT
I have tried without success:
ConnectionManager::drop('default');
ConnectionManager::create('default', $settings);
A print_r(ConnectionManager::create('default', $settings)) return:
[... lots of things ... ]
[config] => Array
(
[persistent] =>
[host] => localhost
[login] => login
[password] => password
[database] => database
[port] => 3306
[datasource] => Database/Mysql
[prefix] =>
)
[... more things ... ]
EDIT 2
Now, I can switch database, but, Company Model always get the old database settings.
FooController.php
<?php
App::uses('AppController', 'Controller');
class FooController extends AppController {
var $uses = array('Foo', 'Company');
public function index() {
echo'<pre>';
print_r($this->Company->find('first')); // Here I get from old settings
print_r($this->Foo->find('first')); // Here I gete from new settings
echo'</pre>';
$this->layout = false;
$this->render(false);
}
}
AppController.php
public function beforeFilter() {
$company = ClassRegistry::init('Company')->find('first');
$settings = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => $company['Company']['dbuser'],
'password' => $company['Company']['dbpass'],
'database' => $company['Company']['dbname'],
'prefix' => ''
);
ConnectionManager::getDataSource('default')->disconnect();
ConnectionManager::drop('default');
ConnectionManager::create('default', $settings);
ConnectionManager::getDataSource('default')->connect();
}
It looks like you need to dynamically create a config and change configs on-the-fly. I don't know if this will work, but it may lead you to the answer.
First we need to create a config for the datasource to use. For this we can use ConnectionManager::create
Next, we need to change configs on-the-fly and for that we can use Model::setDataSource
I haven't tried, but it looks like it should do. If there are issues, leave a comment so I can update the answer.
Edit: This may not work since every model would need to change the to the new datasource. There is a ConnectionManager::drop() method that you could use to drop the default config and then ConnectionManager::create('default', array(...)); to use a new default perhaps.
You can switch databases in your database config file.
Configure your Config/database.php file as follows:
<?php
class DATABASE_CONFIG {
// config for e.g. localhost
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => 'xxxxx',
'database' => 'cake',
'encoding' => 'utf8'
);
// DB config specifically for your domain
public $domain_x = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'username_at_hosting_provider',
'password' => '087bJ#ytvh&^YU#T',
'database' => 'blabla_cake',
'encoding' => 'utf8'
);
public function __construct(){
// switch config depending on domain / env('HTTP_HOST')
if(env('HTTP_HOST')=='domain_x.com'){
$this->default = $this->domain_x;
}
// otherwise keep $this->default
}
}
Edit:
It seems a was a bit too fast answering your question: it does not really cover your question. Sorry!
Okay, so I managed to fix this problem creating the __construct function whithin AppModel and put all the code to drop the default datasource and create another with the new config. Something like this:
AppModel.php
public function __construct($id = false, $table = null, $ds = null)
{
App::uses('CakeSession', 'Model/Datasource');
parent::__construct($id, $table, $ds);
if ($this->useDbConfig == 'default') {
// Change the datasource config
$user = CakeSession::read('Auth.User');
$this->changeDataSource($user);
}
}
public function changeDataSource($config)
{
...
...
...
$dados = array();
$dados['database'] = $config['titulo_bd'];
$dados['host'] = $config['host_bd'];
$dados['login'] = $config['login_bd'];
$dados['password'] = $config['senha_bd'];
$dados['datasource'] = 'Database/Mysql';
$dados['persistent'] = false;
$dados['prefix'] = '';
$dados['encoding'] = 'utf8';
ConnectionManager::create('default', $dados);
ConnectionManager::getDataSource('default')->connect();
}
My case is very similar. In my case, I have two types of connections. One is the default core connection which uses the Config/database.php file for connection configurations. Second one is a dynamic configuration which uses db details saved for logged in user. So, for a few specific actions I have to switch between the default db connection and user based db connection. Here's how it came up.
To switch to user based db connection from default file
try {
ConnectionManager::getDataSource('default')->disconnect();
ConnectionManager::drop('default');
ConnectionManager::create('default', $this->config);
ConnectionManager::getDataSource('default')->connect();
$db = ConnectionManager::getDataSource('default');
} catch (MissingDatabaseException $e) {
$this->Session->setFlash($e->getMessage());
}
where $this->config is user based connection.
To switch back to default file based connection I do:
try {
ConnectionManager::getDataSource('default')->disconnect();
ConnectionManager::drop('default');
$dbconfig = new DATABASE_CONFIG();
ConnectionManager::create('default', $dbconfig->default);
ConnectionManager::getDataSource('default')->connect();
$db = ConnectionManager::getDataSource('default');
}
where $dbconfig->default i.e. $default contains my default connection configuration in Config/database.php
I've just installed CakePHP 2.0.2 for use in a new project. I'm trying to use a database configuration called development but my model doesn't seem to be picking it up.
Based on CakePHP 2's new directory and file name conventions, I've created the following at /app/Model/AppModel.php:
<?php
class AppModel extends Model {
public $useDbConfig = 'development';
}
However, the default home page tells me:
Cake is NOT able to connect to the database.
Yet if I change the configuration name in /app/Config/database.php to default the message changes to a success message, as though it's not picking up my custom AppModel class.
How can I remedy this? As the new CakePHP 2.0 docs say to use the $useDbConfig property as I have done above?
EDIT: Contents of /app/Config/database.php:
class DATABASE_CONFIG {
public $development = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'cakephp_db',
'prefix' => '',
'encoding' => 'utf8',
);
}
Like dhofstet has explained, you still need to have a default config. What I do is add a constructor to the DATABASE_CONFIG class to switch between database configs.
Something like this...
public function __construct()
{
if (DEV_SERVER) {
$this->default = $this->development;
} else {
$this->default = $this->production;
}
}
Your database configuration is very likely correct.
The reason "Cake is NOT able to connect to the database." is shown, is because the script that checks whether it can connect to the database (/lib/Cake/View/Pages/home.ctp) only uses the default database connection for this test. And as there is no such connection, it fails.