2X Cdbconnection = 1Xactiverecord

Hi there!

I long story short…

I’ve read few post:

tip: multiple db connection solution

I need to be able to connect to 2 DB according to the constructor.

[color="#0000FF"]Connection to DB1: [/color]


MyModel::model()->findAllByAttributes(array('id'=>$ID);

[color="#0000FF"]Connection to DB2:[/color]


 

$myModel = new MyModel( null, $tenant_id ) //This one should connect to the DB of my client {tenant_id}.

$myModel->findAllByAttributes(array('id'=>$ID);



[color="#0000FF"]I use 2 componants( DynamicDB, extDynamicDB ) to create 2 singleton CDbConnection[/color]




class TENANT_Page extends CActiveRecord

{

         public $tenantID;

         public $externalTenantDB;

         

         

         

         public function __construct( $scenario = 'insert', $tenantID = null )

         {

                if ( isset($tenantID) )

                {                         

                        $this->tenantID = $tenantID;                        

                }

                parent::__construct($scenario);

         }

         

         

         public function getDbConnection()

         {

                if ( !isset($this->tenantID) )

                { 

                        return Yii::app()->DynamicDB->getTenantConnection();

                } else {

                        $this->externalTenantDB = Yii::app()->extDynamicDB->getTenantConnection($this->tenantID); 

                        // parent::$db = $this->externalTenantDB;

                        return $this->externalTenantDB;

                }       

         }

[...]



[color="#0000FF"]Before using this tenant, I need to verify its existence and fetch it’s connection info.[/color]




class DynamicDB extends CApplicationComponent 

{

        public $connection;

        public $externalTenant;

        

        public function getDbConnection( $IPaddress, $dbName, $username, $password ) 

        {

            if( $this->connection != null)

                    $this->connection->active=false;

            $this->connection=new CDbConnection('mysql:host='.$IPaddress.';dbname='.$dbName, $username, $password);

            $this->connection->active=true;

            return $this->connection;

        }


        

        public function getTenantConnection( $tenantID = null )

        {

                if ( !isset($tenantID))

                {  

                        return Yii::app()->tenant->db;

                } else {        

/*             

                       $sql = "SELECT ID FROM GTBtenant WHERE 'ID'='$tenantID ' ";

                       //$this->externalTenant = GTBtenant::model()->findByPk($tenantID); 

                       $this->externalTenant = new GTBtenant();

                       

                       // THESE ARE 3 WAYS TO CREATE THE SAME DB CONNECTION!

                       //$connection= $this->getDbConnection('host.com','db1','user','password');   

                       $connection = new CDbConnection( 'mysql:host=host.com;dbname=db1', 'user', 'pass' );

                      // $connection = Yii::app()->db;

                       

                       $connection->active = true;

                       $command = $connection->createCommand($sql);

                       $dataReader = $command->query();

                       $connection->active = false;

                       

                       foreach ( $dataReader as $row )

                       {

                                $this->externalTenant->IPaddress    = $row['IPaddress'];

                                $this->externalTenant->DBname       = $row['DBname'];

                                $this->externalTenant->username     = $row['username'];

                                $this->externalTenant->password     = $row['password'];

                       }

*/


                         // THESE ARE RAW INFO FOR TESTING PURPOSES AND IT STILL DOENS'T WORK!!

                       $this->externalTenant = new GTBtenant();

                        $this->externalTenant->IPaddress    = 'host.com';

                        $this->externalTenant->DBname       = 'db1';

                        $this->externalTenant->username     = 'user';

                        $this->externalTenant->password     = 'pass';

                       

                       if ( !isset($this->externalTenant) )

                       {

                                Yii::app()->alert->sendAlert( Alert::ERROR_ALERT, 'the tenant ('. $tenantID .') couldnt be found, when requested by class ['.__CLASS__.']' );

                       } else {                          

                                return $this->getDbConnection(  $this->externalTenant->IPaddress,

                                                                $this->externalTenant->DBname,

                                                                $this->externalTenant->username,

                                                                $this->externalTenant->password );

                       }

                }

        }

        

}



[color="#FF0000"]This is where all hell breaks loose.[/color]

When I go fetch info from the SAAS DB, the connection gets overriden! The function search into the SAAS DB info from the TENANT DB so I can’t save() anything -_-

1- Does making 2 seperate componants for the DB connection a must?

2- How can I keep my second connection to be active?

Thank you again for your time O0

add these lines in /config/main.php


'components'=>array(

.........

        'db'=>array(

            'connectionString' => 'mysql:host=localhost;dbname=database1',

            'emulatePrepare' => true,

            'username' => 'root',

            'password' => 'itsasecret',

            'charset' => 'utf8',

        ),

        'db2'=>array(

            'class' => 'CDbConnection',

            'connectionString' => 'mysql:host=localhost;dbname=database2',

            'emulatePrepare' => true,

            'username' => 'root',

            'password' => 'itsasecret',

            'charset' => 'utf8',

        ),

....

),

Create a new file in protected/components, this example is called AltActiveRecord.php




abstract class AltActiveRecord extends CActiveRecord

{

    const BELONGS_TO='CBelongsToRelation';

    const HAS_ONE='CHasOneRelation';

    const HAS_MANY='CHasManyRelation';

    const MANY_MANY='CManyManyRelation';

    const STAT='CStatRelation';

 

    /**

     * @var CDbConnection the default database connection for all active record classes.

     * By default, this is the 'db' application component.

     * @see getDbConnection

     */

    public static $db;

 

    private static $_models=array();            // class name => model

 

    private $_md;                               // meta data

    private $_new=false;                        // whether this instance is new or not

    private $_attributes=array();               // attribute name => attribute value

    private $_related=array();                  // attribute name => related objects

    private $_c;                                // query criteria (used by finder only)

    private $_pk;                               // old primary key value

 

    /**

     * Returns the database connection used by active record.

     * By default, the "db" application component is used as the database connection.

     * You may override this method if you want to use a different database connection.

     * @return CDbConnection the database connection used by active record.

     */

    public function getDbConnection()

    {

        if(self::$db!==null)

            return self::$db;

        else

        {

 

            // Create CDbConnection and set properties

            self::$db = new CDbConnection();

            foreach(Yii::app()->db2 as $key => $value)

                self::$db->$key = $value;

 

 

        // Uncomment the following lines to prove that you have two database connections

        /*

            CVarDumper::dump(Yii::app()->db);

            echo '<br />';

            CVarDumper::dump(Yii::app()->db2);

            die;

        */

            if(self::$db instanceof CDbConnection)

            {

                self::$db->setActive(true);

                return self::$db;

            }

            else

                throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.'));

        }

    }

}

For each model using the second database,extend the above file example;


/**

 * This is the model class for table "pagedata".

 */

class Pagedata extends AltActiveRecord

{

..

// model stuff here

..

}

Your application should now have access to two databases simultaneously.

Multiple Domains with one entry script

Now for multiple sites (multiple domains) using one installation of Yii and one entry script, example parked domains.

Modify index.php as shown below.




// change the following paths if necessary

$hostname = $_SERVER['SERVER_NAME'];

$yii=dirname(__FILE__).'/../../framework/yii.php';

 

switch ( strtolower($hostname))

{

case 'example1.com';

case 'www.example1.com';

    $config=dirname(__FILE__).'/protected/config/main.php';

    // database 1

break;

case 'example2.com';

case 'www.example2.com';

    $config=dirname(__FILE__).'/protected/config/main2.php';

    // database 2

break;

 

case 'example3.com';

case 'www.example3.com';

    $config=dirname(__FILE__).'/protected/config/useAnyName.php';

    // database 3 

break;

 

default:

$config=dirname(__FILE__).'/protected/config/main.php';

}

// remove the following lines when in production mode

defined('YII_DEBUG') or define('YII_DEBUG',true);

defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);

 

require_once($yii);

Yii::createWebApplication($config)->run();

In each config file you can specify numerous parameters that are site specific. 




// application-level parameters that can be accessed

    // using Yii::app()->params['paramName']

    'params'=>array(

        // this is used in contact page

        'adminEmail'=>'admin@example1.com',

        'googleCode'=>'UA-2****8-11',

        'juitheme'=>'dark-hive',

        'cssfile'=>'cwdi'

    ),



For example styling could be set for each domain name like this (in layout.php)




<link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/<?php echo Yii::app()->params->cssfile; ?>.css" />



I hope this helps

Thank you kumarkulandai for your fast reply!

I was trying to implement your answer but still no success :(

I have a similar code but by the nature of the application, the connection must be dynamic, not only limited to 2DB’s.

here’s my simple code:




$newLayout = new TENANT_Layout(null, $_GET['tenantID'] );

$newLayout->layoutTitle = 'test';

$newLayout->IDGTBtemplate = 4;

$newLayout->save(false);  






class TENANT_Layout extends /*TENANT_ActiveRecord*/ CActiveRecord

{         

         public $externalTenant;     

         

         public function __construct( $scenario = 'insert', $tenantID = null )

         {

                if ( isset($tenantID) )

                {

                        $this->externalTenant = GTBtenant::model()->findByPk( $tenantID ); 

                        if ( !isset( $this->externalTenant) )

                            Yii::app()->alert->sendAlert(Alert::ERROR_ALERT, 'the tenant ('.$tenantID.') couldnt be found, when requested by class ['.__CLASS__.']');  

                }

                parent::__construct( $scenario );

         }

         

         

         public function getDbConnection()

         {

                if ( isset($this->externalTenant) )

                {                       

                        $connection = new CDbConnection(    'mysql:host='. $this->externalTenant->IPaddress.

                                                            ';dbname='. $this->externalTenant->DBname,

                                                             $this->externalTenant->username,

                                                             $this->externalTenant->password);                       

                        $connection->setActive(true);                       

                        return $connection;                        

                }  else {

                         return Yii::app()->tenant->db;

                }      

         }  

[...]



Everything works except that!

When I test:




        protected function beforeSave() 

        {

            parent::beforeSave();

            Yii::app()->toolbox->alert('!!SAVE()!!! layoutTitle: '.$this->layoutTitle.',  IGGTBtemplate: '.$this->IDGTBtemplate);             

            

        }

        

        protected function afterSave() {

            parent::afterSave();

            Yii::app()->toolbox->alert('!!AFTER SAVE()!!! layoutTitle: '.$this->layoutTitle.',  IGGTBtemplate: '.$this->IDGTBtemplate);  

            

        }



I get the right info at beforeSave()… but afterSave() is not triggered…

[color="#2E8B57"]Why would this work:[/color]




class TENANT_Layout extends /*TENANT_ActiveRecord*/ CActiveRecord

{  

[...]


 public function saveCustom(){           

            $table = $this->tableName();             

            $val = '"'.$this->layoutTitle . '","' . $this->IDGTBtemplate.'"';

            

            $dns = $this->getDbConnection()->connectionString;  

            $connection = new CDbConnection( $dns, 'userxxx', 'passxxx');

            $connection->active=true;

             

            $sql = "INSERT INTO $table (layoutTitle, IDGTBtemplate) VALUES ($val);";

           

            $command = $connection->createCommand($sql);

            $command->query();

            $connection->active=false;

            return true;

        }



[color="#FF0000"]And not a simple save()? ???[/color]

Damit! Someone have an idea? :P

Where do you have Yii configured to place trace messages? When you change the return value to false, you are creating an error condition, which is most likely why you are able to see it in the log at that point.

You need to explicitly turn on showing traces in your configuration file, something like:




'log'=>array(

                        'class'=>'CLogRouter',

                        'routes'=>array(

                                array(

                                          'class'=>'CFileLogRoute',

                                          'levels'=>'error, warning',

                                          'logFile' => 'error.log',

                                          ),

                                array(

                                          'class' => 'CFileLogRoute',

                                          'levels' => 'info',

                                          'logFile' => 'application.log',

                                          ),

                                array(

                                          'class' => 'CFileLogRoute',

                                          'levels' => 'trace',

                                          'logFile' => 'debug.log',

                                          ),

)),



and then in your index.php:




// remove the following lines when in production mode

defined('YII_DEBUG') or define('YII_DEBUG',true);

// specify how many levels of call stack should be shown in each log message

defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);



Thank you!

I now have trace messages! But there’s no “INSERT” anywhere…




in /var/www/gtbHosting/protected/extensions/GTBwidgets/LayoutCreator/themes/default/views/editor/index.php (206)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (176)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (161)

2013/06/18 23:57:41 [trace] [system.db.ar.CActiveRecord] GTBtemplate.findByPk()

in /var/www/gtbHosting/protected/extensions/GTBwidgets/LayoutCreator/themes/default/views/editor/index.php (206)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (176)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (161)

2013/06/18 23:57:41 [trace] [system.db.CDbCommand] Querying SQL: SELECT * FROM `GTBtemplate` `t` WHERE `t`.`ID`=4 LIMIT 1

in /var/www/gtbHosting/protected/extensions/GTBwidgets/LayoutCreator/themes/default/views/editor/index.php (206)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (176)

in /var/www/gtbHosting/protected/components/GTBmasterWidget.php (161)

2013/06/18 23:57:41 [trace] [system.db.CDbCommand] Querying SQL: SHOW COLUMNS FROM `GTBlanguage`

in /var/www/gtbHosting/protected/models/GTBlanguage.php (21)

in /var/www/gtbHosting/protected/views/layouts/admin.php (6)

in /var/www/gtbHosting/protected/controllers/SiteController.php (50)

2013/06/18 23:57:41 [trace] [system.db.CDbCommand] Querying SQL: SHOW CREATE TABLE `GTBlanguage`

in /var/www/gtbHosting/protected/models/GTBlanguage.php (21)

in /var/www/gtbHosting/protected/views/layouts/admin.php (6)

in /var/www/gtbHosting/protected/controllers/SiteController.php (50)

2013/06/18 23:57:41 [trace] [system.db.ar.CActiveRecord] GTBlanguage.findByAttributes()

in /var/www/gtbHosting/protected/views/layouts/admin.php (7)

in /var/www/gtbHosting/protected/controllers/SiteController.php (50)

in /var/www/gtbHosting/protected/components/Controller.php (97)

2013/06/18 23:57:41 [trace] [system.db.CDbCommand] Querying SQL: SELECT * FROM `GTBlanguage` `t` WHERE `t`.`i18n`=:yp0 LIMIT 1

in /var/www/gtbHosting/protected/views/layouts/admin.php (7)

in /var/www/gtbHosting/protected/controllers/SiteController.php (50)

in /var/www/gtbHosting/protected/components/Controller.php (97)



Not really a good sign when I try to create a new record. <_<

[color="#00FF00"]the saveCustom() is working perfectly[/color].

[color="#FF0000"]the save(false) is not working.[/color]

I don’t understand the fundamental differences… why would one work and not the other!?