Multiple Databases In One Model

I am trying to query multiple databases (same table, same structure, just different databases) in a model file. I am using the "mpgii" extension to create the models. Then I override the connectionId() function like this:


public function connectionId()

	{

		return array('db_1','db_2');

	}

I know I can’t pass arrays to the MultiActiveRecord class so I changed the getDbConnection function to check if the returned argument is an array and if it is, get an instance for each connection like this:


$dbNameArray=$this->connectionId();

		if (is_array($dbNameArray)) {

			foreach ($dbNameArray as $dbName) {

				//var_dump($dbName);

				if(!isset(self::$db[$dbName])){

					if(Yii::app()->hasComponent($dbName) && (self::$db[$dbName]=Yii::app()->getComponent($dbName)) instanceof CDbConnection){

						self::$db[$dbName]->setActive(true);

					}else

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

				}

			}

		} else {

			$dbName=$this->connectionId();

			//var_dump($dbName);

			if(!isset(self::$db[$dbName])){

				if(Yii::app()->hasComponent($dbName) && (self::$db[$dbName]=Yii::app()->getComponent($dbName)) instanceof CDbConnection){

					self::$db[$dbName]->setActive(true);

				}else

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

			}

				

			return self::$db[$dbName];

		}

But when I try to access the page using the model I get this error:


Fatal error: Call to a member function getSchema() on a non-object in C:\xampp\htdocs\yii\framework\db\ar\CActiveRecord.php on line 2309

What am I doing wrong?

may be this scenario is not suitable for using the AR . try DAO .

or just use one “db” component to query partial result set , then switch to another “db1” compoent fetch another partial row set , then make a merging ! :lol:

The second method that you suggested seems easier to me so how do I switch from one component to the other? As I said I can only return ONE component from connectionID :confused:

hehe :lol:




   $oldDb = Yii::app()->db;

   //switch to anther db:

   Yii::app()->setComponent('db',Yii::app()->db2);

   // then use your ar to do the stuff !

   ....

   // after query , switch back :

   Yii::app()->setComponent('db',$oldDb);






i have not met such things so the code just my thought , try it yourself !

Several days have passed since I fixed this so I thought I would post my solution:

  1. Define multiple db components in config/main.php

  2. In the model create a random public static variable like $dbName and a static array who will contain the databases that the model will search in

  3. Modify the search function to use CArrayDataProvider by merging the returned model arrays like this:




// Get data from all databases

		foreach (self::$dbList as $dbItem) {

			self::$dbName = $dbItem;

			$$dbItem = Order::model()->findAll($criteria);

		}


		// NOTE: To add more databases add a variable in array_merge with the name of the db component e.g."db_3"

		$data = new CArrayDataProvider(array_merge($db_1, $db_2), array(

				'keyField'=>'order_id',

				'sort'=> array(

						'attributes'=> array(

								'order_id'=>array(

										'DESC'=>'order_id DESC',

								),

								'date_modified'=>array(

										'ASC'=>'date_modified DESC'

								),

								'*',

						),

						'defaultOrder'=>'order_id DESC'

				),

				'pagination'=> array(

						'pageSize'=>10,

				),

		));



  1. Modify the connectionId() function like this:



public function connectionId()

	{

		if (self::$dbName === null) {

			return self::$defaultDb;

		} else {

			return self::$dbName;

		}

	}



  1. Finally modify controller functions to redirect you to appropriate db by changing the public static variable of the model.

e.g.:




public function actionView($id,$store)

	{

		if ($store==='blah1') {

			Order::$dbName='db_1'; // This is the one you care about

		} elseif ($store==='blah2') {

			Order::$dbName='db_2';

		}

		$this->render('view',array(

				'model'=>$this->loadModel($id),

		));

	}



(In my case I check a model attribute that is unique to each db)

That’s it. It doesn’t look very clean to me but it does the trick.