É possível criar aplicação com vários bancos de dados no Yii ?

É possível desenvolver uma aplicação com vários bancos de dados no Yii ?

Por exemplo, se eu tenho 10 bancos com estrutura exatamente igual e dados diferentes, posso criar uma só aplicação para manusear todos os 10 bancos?

Nesse caso, como eu devo configurar o arquivo main.php da pasta config ?

Sim, com certeza!

Abaixo um exemplo de configuração no main.php




        'db' => array(

            'connectionString' => '<sua string de conexão>',

            // Outras configurações

        ),

        'secondDb' => array(

            'class'            => 'CDbConnection' // Isso é o que faz com que o componente seja uma conexão a outro banco.

            // O resto é igual...

            'connectionString' => '<sua string de conexão>',

            // Outras configurações

        ),

Para chamar é só informar o nome do DB.


Yii::app()->db

Yii::app()->secondDb

Onde é que eu faço essa chamada ?

Sempre que você precisar recuperar dados do banco A ou B, ou seja, nas actions dos seus controllers.

Lothor,

Então a Action ficaria assim:




public function actionIndex()

{

        Yii::app()->secondDb;

        $content = Content::model()->findAllBySql("SELECT * FROM content WHERE id = '1' AND status = '1' LIMIT 1");

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

            'content' => $content

        ));

}



É isso ? ?

O código abaixo:


$connection = Yii::app()->secondDb;

Deve ser usado quando você quiser utilizar alguma função do Yii para realizar uma consulta na própria ação do Controlador, como por exemplo a "CreateCommand", no seu caso, podes fazer a troca de banco, diretamente no modelo, para isso, implemente a função "getDbConnection" em seu modelo:




public function getDbConnection() {

	return Yii::app()->secondDb;

}



Com isso, não precisarás abrir a conexão em sua ação.

Pode dar um exemplo prático? Por exemplo, como ficaria o model e controller?

Humm… Um exemplo prático? Vamos lá!

Não vou fazer nada demais porque ando meio sem tempo, mas vou tentar exemplificar.

Suponha que eu tenha uma empresa e nessa empresa eu tenha várias aplicações/sites.

Cada aplicação/site, terá o seu banco próprio e cada um terá as suas características próprias, ou seja, podem ser feitos em quaisquer linguagens de programação com quaisquer tecnologias, afinal cada projeto tem suas necessidades individuais.

Agora, eu enquanto dono, quero criar um sistema que administre as principais informações de todos os meus sistemas em uma única aplicação, porque se não, para cada projeto eu vou ter um painel de administração para realizar os controles e configurações necessárias.

Imagine algo simples, só como exemplo.

Algo do tipo…

  • Quantidade de usuários on-line em cada sistema.

  • Quem pagou, quem deixou de pagar.

  • etc.

Para resolver esse caso você pode criar uma aplicação simples no Yii, configurando os bancos de dados, gerando os models específicos de cada banco e os utilizando normalmente a medida que a necessidade surgir.

Com o cenário montado, basicamente a sua aplicação teria as seguintes modificações…

main.php




// [...]


'components' => array(

	// [...]

	

	// DB Principal (SQLite)

	'db' => array( // Como o nome do componente é "db" por padrão já utiliza a classe "CDbConnection"

		'connectionString' => 'sqlite:protected/data/blog.db',

		'tablePrefix' => 'tbl_',

	),

		

	// DB da 1ª Aplicação (Postgres)

	'application1' => array(

		'class'=>'CDbConnection', // Faz com que o componente seja uma Conexão com o Banco 

		'connectionString' => 'pgsql:host=localhost;port=5432;dbname=you_db_name',

		'emulatePrepare' => true,

		'username' => 'you_username',

		'password' => 'you_password',

		'charset' => 'utf8',

	),

		

	// DB da 2ª Aplicação (MySQL)

	'application2' => array(

		'class'=>'CDbConnection', // Faz com que o componente seja uma Conexão com o Banco

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

		'emulatePrepare' => true,

		'username' => 'you_username',

		'password' => 'you_password',

		'charset' => 'utf8',

	),

		

	// [...]

),


// [...]



Com o código acima, já conseguimos abrir uma conexão com os nossos três bancos de dados e não só isso, com três bancos de dados diferentes.

Até aí tudo bem… Mas supondo que as 3 aplicações possuem uma tabela chamada "tbl_user", como vamos criar três modelos diferentes cada um capturando os dados de um banco diferente e para chamar isso nos controladores da aplicação?

Vamos lá…

Os models seriam basicamente o seguinte… (lembre-se que os nomes utilizados são só exemplos)

UserAdm.php


class UserAdm extends CActiveRecord

{


	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	public function tableName()

	{

		return 'tbl_user';

	}


	public function rules()

	{

		// [...]

	}


	public function relations()

	{

		return array(

			// [...]

		);

	}


	public function attributeLabels()

	{

		return array(

			// [...]

		);

	}


	public function search()

	{

		$criteria=new CDbCriteria;


		// [...]


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}

}

UserApp1.php


class UserApp1 extends CActiveRecord

{


	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/*

	 * Sobrescrevendo a função da classe extendida para que ao invés

	 * de se pegar a conexão com o DB padrão, cujo componente é "db",

	 * pegar a conexão do DB que este modelo corresponde. 

	 */

	public function getDbConnection() {

		// Aqui eu digo que os dados deste modelo devem ser pêgos no DB da 1ª APP

		return Yii::app()->application1;

	}

	

	public function tableName()

	{

		return 'tbl_user';

	}


	public function rules()

	{

		// [...]

	}


	public function relations()

	{

		return array(

			// [...]

		);

	}


	public function attributeLabels()

	{

		return array(

			// [...]

		);

	}


	public function search()

	{

		$criteria=new CDbCriteria;


		// [...]


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}

}

UserApp2.php


class UserApp2 extends CActiveRecord

{


	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/*

	 * Sobrescrevendo a função da classe extendida para que ao invés

	 * de se pegar a conexão com o DB padrão, cujo componente é "db",

	 * pegar a conexão do DB que este modelo corresponde. 

	 */

	public function getDbConnection() {

		// Aqui eu digo que os dados deste modelo devem ser pêgos no DB da 2ª APP

		return Yii::app()->application2;

	}

	

	public function tableName()

	{

		return 'tbl_user';

	}


	public function rules()

	{

		// [...]

	}


	public function relations()

	{

		return array(

			// [...]

		);

	}


	public function attributeLabels()

	{

		return array(

			// [...]

		);

	}


	public function search()

	{

		$criteria=new CDbCriteria;


		// [...]


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}

}

Como já deves ter percebido, a função "getDbConnection()" não foi adicionada no "UserAdm.php" pois o retorno padrão dela já é o banoc de dados padrão.


Yii::app()->getDb() // Acho que é isso, estou com preguiça de olhar

Aí, para buscar esses valores no Controlador, não há necessidade de abrir conexão, afinal o próprio modelo já faz isso, executando o comando abaixo, os resultados retornados, serão diferentes:




$admUsers = UserAdm::model()->findAll();

$app1Users = UserApp1::model()->findAll();

$app2Users = UserApp2::model()->findAll();



Cada um retornará os dados da "tbl_user" de um DB diferente.

O restante é igual, lembrando que com isso, você ainda pode aproveitar e usas todo o poder da Orientação a Objeto se assim desejar, criando uma classe mais genérica chamada User que estende de CActiveRecord e as especializadas (UserAdm, UserApp1, UserApp2) estendendo de User.

Enfim, espero ter ajudado, abraço e desculpa a demora na resposta.

Obrigado pela ajuda Lothor.

Mas infelizmente a aplicação continua com erros. Está aparacendo este erro aqui:


Object configuration must be an array containing a "class" element.

Muito estranho, pois eu modifiquei o getDbConnection no model.

Finalmente!!!!!!

Agora deu certo.

Faltava apenas um parâmetro na configuração dos bancos adicionais. O parâmetro que faltava era este: ‘class’ => ‘CDbConnection’

Assim, a configuração dos bancos ficou assim:




'db'=>array(

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

			'emulatePrepare' => true,

			'username' => 'root',

			'password' => '1234',

			'charset' => 'utf8',

		),

        'db2'=>array(

			'connectionString' => 'mysql:host=ip2;dbname=banco2',

			'emulatePrepare' => true,

			'username' => 'root',

			'password' => '1234',

			'charset' => 'utf8',

                        'class' => 'CDbConnection'

		),

        'db3'=>array(

			'connectionString' => 'mysql:host=ip3;dbname=banco3',

			'emulatePrepare' => true,

			'username' => 'root',

			'password' => '1234',

			'charset' => 'utf8',

                        'class' => 'CDbConnection'

		),



Exatamente, conforme informei acima, apenas o componente "db" recebe por padrão a classe CDbConnection, os demais temos que setar, pois não tem como o framework identificar que o componente é utilizado para conectar com o banco, só setando a classe do componente é que isso é feito.