This article is what I hope will be the first in a series about my favorite way to develop web applications, the Zend Framework. I’m not going to try and sell the framework to you, I’m going to assume you already know all about it and that’s how you got here.
The Zend_Db_Table class provides a powerful and easy-to-use way of abstracting your data model into PHP objects. One problem I recently encountered was that the design of the class assumes you’re only going to use one database, which may not be the case.
Let’s assume you’ve got a Zend application that follows the de facto standard laid out in this tutorial. You’d have something in your application/config.ini file that looked like this:
-
-
[general]
-
-
db.adapter = PDO_MYSQL
-
-
db.config.host = localhost
-
-
db.config.username = zfuser
-
-
db.config.password = ninjabunnies
-
-
db.config.dbname = zftest
-
-
db.config.port = 3306
Then in your index.php bootstrapper, you’d have something like this:
-
-
Zend_Loader::loadClass(‘Zend_Controller_Front’);
-
-
Zend_Loader::loadClass(‘Zend_Registry’);
-
-
Zend_Loader::loadClass(‘Zend_Config_Ini’);
-
-
Zend_Loader::loadClass(‘Zend_Db’);
-
-
Zend_Loader::loadClass(‘Zend_Db_Table’);// load configuration
-
-
$config = new Zend_Config_Ini(‘./application/config.ini’, ‘general’);
-
-
Zend_Registry::set(‘config’, $config);
-
-
// setup database
-
-
$dbAdapter = Zend_Db::factory($config->db->adapter,
-
-
$config->db->config->toArray());
-
-
Zend_Db_Table::setDefaultAdapter($dbAdapter);
-
-
Zend_Registry::set(‘dbAdapter’, $dbAdapter);
In the above example, we set all the database parameters in the config.ini file. We load these values into dbAdapter and decalre that adapter as the default via the setDefaultAdapter method of Zend_Db_Table. Any classes derived from Zend_Db_Table abstract will use this database parameter. This works fine for one database, but what if we’ve got more than one? We can’t really use the default adapter anymore, at least not in the usual way.
One way to get around this problem is to create an array of adapters and make the key of the array the name of the database. We can then implement an application specific table class that will allow us to easily choose the adapter we want to use. We can then have our application table classes extend that.
For this example, let’s say we’re building a site to sell textbooks, and there’s two databases: library and order_system
Your config.ini would now look something like this:
-
-
[databases]
-
-
db.library.adapter = PDO_MYSQL
-
-
db.library.config.host = localhost
-
-
db.library.config.username = libraryuser
-
-
db.library.config.password = bunch0crunch
-
-
db.library.config.dbname = library
-
-
db.library.config.port = 3306
-
-
db.library.default = truedb.order_system.adapter = PDO_MYSQL
-
-
db.order_system.config.host = localhost
-
-
db.order_system.config.username = ecommerceuser
-
-
db.order_system.config.password = showMeThe$$$
-
-
db.order_system.config.dbname = order_system
-
-
db.order_system.config.port = 3306
The in your index.php bootstrapper, you would do something more like the following:
-
-
Zend_Loader::loadClass(‘Zend_Controller_Front’);
-
-
Zend_Loader::loadClass(‘Zend_Registry’);
-
-
Zend_Loader::loadClass(‘Zend_Config_Ini’);
-
-
Zend_Loader::loadClass(‘Zend_Db’);
-
-
Zend_Loader::loadClass(‘Zend_Db_Table’);// setup databases
-
-
$databases = new Zend_Config_Ini(‘./application/config.ini’, ‘databases’);
-
-
-
foreach($databases->db as $config_name => $db){
-
-
$dbAdapters[$config_name] = Zend_Db::factory($db->adapter,
-
-
$db->config->toArray());
-
-
if((boolean)$db->default){
-
-
Zend_Db_Table::setDefaultAdapter($dbAdapters[$config_name]);
-
-
}
-
-
}
-
-
Zend_Registry::set(‘dbAdapters’, $dbAdapters);
All we’re doing here is creating an array of adapters instead of just one. If we set the parameter “default” on one of them to true, that will become our default database. This will save you a bit of typing later if have one database that you’ll use most often and one or more other ones you use less.
Then we create a new class, called App_Db_Table_Abstract. You can put this in /application/models folder, but I like to create a folder called /application/lib for my abstract classes. Either way, just make sure it ends up in your include path.
-
abstract class App_Db_Table_Abstract extends Zend_Db_Table
-
-
{
-
-
function App_Db_Table_Abstract($config = null){
-
-
-
$dbAdapters = Zend_Registry::get(‘dbAdapters’);
-
-
$config = ($dbAdapters[$this->_use_adapter]);
-
-
}
-
-
return parent::__construct($config);
-
-
}
-
-
}
All we’re doing here is looking for a class property called “$_use_adapter”. If that’s set (in our derived classes) then we’ll look for that dbAdapter in the registry. If not, we’ll use whatever the default is set to.
Here’s an example of a derived class that would do this. It uses the “orders” table in the “order_system” database.
-
class Orders extends App_Db_Table_Abstract {
-
-
protected $_name = ‘groups’;
-
-
protected $_primary = ‘group_id’;
-
-
protected $_use_adapter = ‘order_system’;
-
-
}
Now this class will use the correct adapater instead of the default. That’s all there is to it!
This method of implementation is nice because you could have one database, two databases or a hundred databases and the only thing you’d need to do would be to add additional sections to the config.ini file.
January 16th, 2008 at 2:27 am
[...] Jaybill.com » Blog Archive » Using the Zend Framework with Multiple Databases (tags: webdev zend php database scalability) [...]
April 3rd, 2008 at 11:26 pm
dear sir,
i’m is student from indonesia and i’m beginer in php, i try to study zf.
i have question, how to connect 2 db with zf, for example Ms. SQL server (main db) and MySQL (2nd db).
i use ms. sql to get master data, and i save it to mysql.
thx’s f u help
and sorry my english is to bad.
June 3rd, 2008 at 3:07 am
Excellent article, solved my multi database problems immediately, thanks a lot
June 17th, 2008 at 5:54 am
great!
thanks!
July 11th, 2008 at 7:50 am
App_Db_Table_Abstract this name is a bit confusing, you’d assume (from the ZF naming convention) that it is found in app/db/table/abstract.php
What happens if you have 2 DBs both with a ‘foo’ table? Do you put your models in app/model/db/foo.php ?
In my app I read the ini file/db section in the controller class. This means each different controller can access a different database. But your way is an interesting approach.
monk.e.boy
July 15th, 2008 at 10:52 am
@monk.e.boy - Thanks for the response! Having multiple tables with the same name in different databases shouldn’t be an issue at all - you’d just give them different class names.
As for your other assertion, I’m not sure why you’d want controllers to know anything about the databases - that pretty much violates the MVC pattern.