Работа с несколькими БД в Zend Framework и с иcпользованием Zend_Application

Как работать с несколькими БД в ZF? Давайте посмотрим, начнем с конфигурационного файла. Вы ведь используете компонент Zend_Application? Тогда поехали.

Будем придерживаться правильного подхода в разработке приложений. Давайте создадим такой ini файл с настройками для Zend_Application. В конфиге будет одна общая секция, и 3 секции для разных сред выполнения нашего приложения - development, test и production. В каждой из этих секций мы сможем работать с несколькими БД одновременно.

Смотрим конфиг:

; Общие настройки
[bootstrap]
  ; Подключение к БД #1
  resources.dbs.db1.adapter                    = "pdo_mysql"
  resources.dbs.db1.params.host                = "localhost"
  resources.dbs.db1.isDefaultTableAdapter      = true
  resources.dbs.db1.params.driver_options.1002 = "SET NAMES utf8"
  resources.dbs.db1.params.profiler.enabled    = true
  resources.dbs.db1.params.profiler.class      = Zend_Db_Profiler_Firebug

  ; Подключение к БД #2
  resources.dbs.db2.adapter                    = "pdo_mysql"
  resources.dbs.db2.params.host                = "localhost"
  resources.dbs.db2.isDefaultTableAdapter      = false
  resources.dbs.db2.params.driver_options.1002 = "SET NAMES utf8"
  resources.dbs.db2.params.profiler.enabled    = true
  resources.dbs.db2.params.profiler.class      = Zend_Db_Profiler_Firebug

; Настройки проекта в стадии development
[development : bootstrap]
  ; Подключение к БД #1
  resources.dbs.db1.params.dbname   = "mydatabase_development"
  resources.dbs.db1.params.username = "root"
  resources.dbs.db1.params.password = ""

  ; Подключение к БД #2
  resources.dbs.db2.params.dbname   = "mydatabase2_development"
  resources.dbs.db2.params.username = "root"
  resources.dbs.db2.params.password = ""

; Настройки проекта в стадии test
[test : bootstrap]
  ; Подключение к БД #1
  resources.dbs.db1.params.dbname   = "mydatabase_test"
  resources.dbs.db1.params.username = "root"
  resources.dbs.db1.params.password = ""

  ; Подключение к БД #2
  resources.dbs.db2.params.dbname   = "mydatabase2_test"
  resources.dbs.db2.params.username = "root"
  resources.dbs.db2.params.password = ""

; Настройки проекта в стадии production
[production : bootstrap]
  ; Подключение к БД #1
  resources.dbs.db1.params.dbname   = "mydatabase_production"
  resources.dbs.db1.params.username = "root"
  resources.dbs.db1.params.password = ""

  ; Подключение к БД #2
  resources.dbs.db2.params.dbname   = "mydatabase2_production"
  resources.dbs.db2.params.username = "root"
  resources.dbs.db2.params.password = ""

В общей секции (bootstrap) мы определяем общие настройки подключения к БД. В остальных секциях, мы указываем различные БД и реквизиты доступа, в зависимости от типа секции. Итак, на каждую стадию разработки приложения, такие как непосредственная разработка, тестирование, пуск в эксплуатацию у нас есть своя БД (или несколько, если требуется работать с несколькими БД одновременно).

Видели в конфиге ресурс dbs? Да, у нас нет такого ресурса. Придется его создать. Я взял этот код с сайта blog.keppens.biz.

Вот, как выглядит ресурс, дающий нам возможность работать с несколькими БД:

/**
 * Amazium Library
 *
 * @category   Amz
 * @package    Amz_Application
 * @subpackage Resource
 * @copyright  Copyright (c) 2009 Amazium (http://www.amazium.be)
 */

/**
 * Resource for creating multiple database adapters
 *
 * @uses       Zend_Application_Resource_Base
 * @category   Amz
 * @package    Amz_Application
 * @subpackage Resource
 * @copyright  Copyright (c) 2009 Amazium (http://www.amazium.be)
 */
class Project_Application_Resource_Dbs extends Zend_Application_Resource_ResourceAbstract
{

    /**
     * Adapter to use
     *
     * @var array
     */
    protected $_db = array();

    /**
     * Default adapter
     *
     * @var boolean
     */
    protected $_defaultDb = null;

    /**
     * Adapter type to use
     *
     * @return string
     */
    public function getAdapter($db)
    {
        $db = $this->isValidDb($db);
        if (isset($this->_options[$db]['adapter'])) {
            return $this->_options[$db]['adapter'];
        }
        return null;
    }

    /**
     * Adapter parameters
     *
     * @return array
     */
    public function getParams($db)
    {
        $db = $this->isValidDb($db);
        if (isset($this->_options[$db]['params'])) {
            return $this->_options[$db]['params'];
        }
        return array();
    }

    /**
     * Is this adapter the default table adapter?
     *
     * @return void
     */
    public function isDefaultTableAdapter($db)
    {
        $db = $this->isValidDb($db);
        if (isset($this->_options[$db]['isDefaultTableAdapter'])) {
            return $this->_options[$db]['isDefaultTableAdapter'];
        }
        return false;
    }

    /**
     * Retrieve initialized DB connection
     *
     * @return null|Zend_Db_Adapter_Interface
     */
    public function getDbAdapter($db = null)
    {
        // check if the DB is valid
        $db = $this->isValidDb($db, true);
        if (is_null($db) && is_null($db = $this->_defaultDb)) {
            return null;
        }

        if ((!isset($this->_db[$db]) || (null === $this->_db[$db]))
            && (null !== ($adapter = $this->getAdapter($db)))
        ) {
            $this->_db[$db] = Zend_Db::factory($adapter, $this->getParams($db));
        }
        return $this->_db[$db];
    }

    /**
     * Defined by Zend_Application_Resource_IResource
     *
     * @return void
     */
    public function init()
    {
        if (is_null($this->_defaultDb)) {
            $options = $this->getOptions();
            $defaultDb = null;
            foreach ($options as $db=>$dbOptions) {
                if (null !== ($adapter = $this->getDbAdapter($db))) {
                    if ($this->isDefaultTableAdapter($db) || is_null($defaultDb)) {
                        $defaultDb = $db;
                    }
                }
            }
            if (!is_null($defaultDb)) {
                $this->_defaultDb = $defaultDb;
                Zend_Db_Table::setDefaultAdapter($this->getDbAdapter($defaultDb));
            }
        }
    }

    /**
     * Check if a database key is valid
     *
     * @param string $db
     * @param boolean $revertToDefaultDb
     * @return string
     */
    public function isValidDb($db, $revertToDefaultDb = false)
    {
        $db = strtolower(trim($db));
        if (!in_array($db, array_keys($this->_options))) {
            if (!$revertToDefaultDb) {
                $db = $this->_defaultDb;
            } else {
                throw new Zend_Application_Resource_Exception('Invalid database specified');
            }
        }
        return $db;
    }

}

Теперь, в нашем файле Bootstrap, нам нужно запустить наш ресурс:

class Project_Application_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    /**
     * Initialize connections to multiple db's
     *
     * @return void
     */
    protected function _initDbs()
    {
        $this->bootstrap('dbs');
        $dbs = $this->getPluginResource('dbs');

        Zend_Registry::set('db', $dbs->getDbAdapter('db1'));
        Zend_Registry::set('db2', $dbs->getDbAdapter('db2'));

        return $dbs;
    }
}

Как видно, мы поместили адаптеры БД в реестр, откуда можем получить их в любой части приложения. По-умолчанию будет использоваться адаптер №1 - db, мы указали это в конфиге при помощи опции isDefaultTableAdapter. То есть, например, Zend_Db_Table по-умолчанию будет работать именно с первым адаптером.

Это все, что вам нужно сделать, чтобы работать с несколькими БД в Zend Framework.
Спрашивайте в комментах, если что-то непонятно.

Also interesting

Tags: , ,

4 Responses to “Работа с несколькими БД в Zend Framework и с иcпользованием Zend_Application”

  1. Yaroslav Vorozhko says:

    Вы показали как сделать несколько подключений, но неправильно сказали про метод их применения.
    Из вашего конфига видно, что разные подключения вы используете для разного окружения: test, development, production. Для этого нет необходимости создавать несколько подключений, достаточно при инициализации указать какой тип бд использовать.
    В общем интересует вопрос зачем вам несколько подключений?
    И как происходит выбор необходимого подключения (адаптера) в модели?

    • Ouch! says:

      Ярослав, что-то не очень понятно, о чем вы.
      О том, что плохо создавать сразу несколько подключений в бутстрапе?
      В общем, расскажите, пожалуйста, подробнее.

      • lcf says:

        Ты пишешь:

        В общей секции (bootstrap) мы определяем общие настройки подключения к БД. В остальных секциях, мы указываем различные БД и реквизиты доступа, в зависимости от типа секции. Итак, на каждую стадию разработки приложения, такие как непосредственная разработка, тестирование, пуск в эксплуатацию у нас есть своя БД.

        Однако расказываешь о другом, о работе с двумя объектами подключения бд. На каждую стадию разработки приложения у нас допустим и своя бд но объект то один и тотже. Поэтому ваще обычно люди ограничиваются различными секциями в конфигурационном файле.

        Типа (кусок аппликейшн ини)

        [production]
        ;
        ; Database initialization
        ;

        resources.db.adapter = “PDO_MYSQL”
        resources.db.params.host = “mysql.notes-notes.com”
        resources.db.params.dbname = “notes”
        resources.db.params.username = “notes-notes”
        resources.db.params.password = “supersecretpass”

        [development : production]
        ;
        ; For development, we want to display errors and use other database
        ;

        phpSettings.display_startup_errors = 1
        phpSettings.display_errors = 1
        phpSettings.error_reporting = E_ALL

        resources.db.params.host = “mysql.dev.notes-notes.com”
        resources.db.params.dbname = “devnotes”
        resources.db.params.username = “lcf”
        resources.db.params.password = “iamadeveloper”

        ***

        Хотя в принципе два подключения то допустим и могут понадобится, ситуация вполне реальная, когда например просто приложение работает с разными уже существующими бд или чо нить в этом духе, так что пост вполне может быть полезен, но аргументация не правильная.

        • Ouch! says:

          Да, я понял что вам не понравилось. На самом деле, такой разбитый на несколько “сред” ini-конфиг я привел лишь для того, чтобы продвигать правильный стиль разработки в массы. Я мог, конечно, обойтись и одной секцией, чтобы показать, как работать с несколькими БД одновременно.

          Пост я подправил немного.

Leave a Reply