Я написал приложение, в котором мастер-панель администратора могла бы использоваться для создания и администрирования нескольких обращенных к клиенту «экземпляров», поэтому была необходимость «направлять» запросы, выполняющиеся внутри мастер-приложения, на любой из экземпляров.конкретные базы данных.Я проиллюстрирую урезанную версию того, что я сделал сначала (которая не так требовательна, как ваша цель), и затем представлю более мощный подход.
Использование нескольких баз данных для всех запросов
Направлениезапросы к базе данных, которая была указана заранее, просты: просто переопределите метод CActiveRecord::getDbConnection
.То, что я сделал, можно обрезать так:
abstract class InstanceActiveRecord extends CActiveRecord {
public static $dbConnection = null;
public function getDbConnection() {
if (self::$dbConnection === null) {
throw new CException('Database connection must be defined to work with instance records.');
}
return self::$dbConnection;
}
}
Так что, если вы хотите направить все операции в конкретную базу данных, вам просто нужно извлечь ваши модели ActiveRecord из InstanceActiveRecord
вместо CActiveRecord
, а затемпросто сделайте InstanceActiveRecord::dbConnection = $connection
и все готово.
Использование нескольких баз данных с автоматическим выбором на основе типа запроса
Для этого вам нужно углубиться в CActiveRecord
.Оказывается, что getDbConnection
в основном используется getCommandBuilder
, который, в свою очередь, является методом, вызываемым всеми семействами удаления / обновления / вставки.Поэтому нам нужно передать некоторый контекст из этих функций до getDbConnection
, где будет сделан выбор, какое соединение мы хотим использовать.
Для этого нам придется переопределить все методыв этих семействах разумным подходом может быть:
Шаг 1. Добавьте необязательный параметр в getDbConnection
и переопределите его, чтобы вернуть любое соединение, которое вы хотите, на основе значения параметра,Самым простым будет что-то вроде этого:
public function getDbConnection($writeContext = null) {
if ($writeContext === null) {
return parent::getDbConnection(); // to make sure nothing will ever break
}
// You need to get the values for $writeDb and $readDb in here somehow,
// but this can be as trivially easy as you like (e.g. public static prop)
return $writeContext ? $writeDb : $readDb;
}
Шаг 2. Добавьте необязательный параметр к getCommandBuilder
с той же семантикой и переопределите его для пересылки значения:
public function getCommandBuilder($writeContext = null) {
return $this->getDbConnection($writeContext)->getSchema()->getCommandBuilder();
}
Шаг 3. Найдите все сайты вызовов: getCommandBuilder
(их будет несколько) и getDbConnection
(их было только на 2 больше, чем внутри getCommandBuilder
ввремя я посмотрел) и переопределить их, чтобы указать контекст чтения / записи соответствующим образом.Пример:
public function deleteAll($condition='',$params=array()) {
Yii::trace(get_class($this).'.deleteAll()','system.db.ar.CActiveRecord');
// Just need to add the (true) value here to specify write context:
$builder=$this->getCommandBuilder(true);
$criteria=$builder->createCriteria($condition,$params);
$command=$builder->createDeleteCommand($this->getTableSchema(),$criteria);
return $command->execute();
}
После этого вы должны быть готовы к работе.Также ничто не мешает вам создать более сложный механизм выбора контекста, чем показанная здесь опция true
/ false
, концепция та же.
Практические проблемы
Хотя все этодля достижения поставленной цели идеально, остается вопрос в отношении удобства поддержки этого подхода.
Это правда, что этот маршрут будет включать в себя много кода копирования / вставки из CActiveRecord
, что не идеально, если естьвозможность перенести ваше приложение на более позднюю версию фреймворка позже;Для этого вы будете вынуждены синхронизировать ваш подкласс с последней версией CActiveRecord
.
. Чтобы перенести это и сделать вашу жизнь проще в будущем, вы можете рассмотреть такой подход:
- Вместо копирования / вставки и переопределения только части
CActiveRecord
, сделайте точную копию (за исключением, конечно, свойств) CActiveRecord
и внесите в нее изменения.Другими словами, скопируйте даже те методы, которые вы не намереваетесь переопределить. - Выполните изменения, упомянутые выше.Помните, что это включает в себя переопределение
getDbConnection
и только незначительные правки для дюжины или двух других мест . - Заставьте ваши модели расширять результирующий класс.
Теперь, когда придет время перейти на более позднюю версию Yii, вам необходимо снова синхронизировать ваш класс с CActiveRecord
.Запустите ваш любимый инструмент сравнения и сравните ваш класс с целевой версией CActiveRecord
.Инструмент сравнения покажет вам только getDbConnection
и незначительные правки, плюс любые изменения, внесенные в CActiveRecord
в ядре Yii.Скопируйте эти другие изменения в свой класс.Задача решена за 5 минут до вершины.