Как использовать разные соединения в соответствии с типом запроса в Yii - PullRequest
2 голосов
/ 15 сентября 2011

Все мои чтения должны идти на одно соединение с БД Все мои записи должны идти в другое соединение

Как мне это сделать в Yii с минимальным изменением кода базовой библиотеки?

И в некоторых случаях (как указано в комментариях) мне понадобится возможность контролировать каждый тип соединения, поэтому чтение может перейти и к Мастеру.

1 Ответ

4 голосов
/ 15 сентября 2011

Я написал приложение, в котором мастер-панель администратора могла бы использоваться для создания и администрирования нескольких обращенных к клиенту «экземпляров», поэтому была необходимость «направлять» запросы, выполняющиеся внутри мастер-приложения, на любой из экземпляров.конкретные базы данных.Я проиллюстрирую урезанную версию того, что я сделал сначала (которая не так требовательна, как ваша цель), и затем представлю более мощный подход.

Использование нескольких баз данных для всех запросов

Направлениезапросы к базе данных, которая была указана заранее, просты: просто переопределите метод 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.

. Чтобы перенести это и сделать вашу жизнь проще в будущем, вы можете рассмотреть такой подход:

  1. Вместо копирования / вставки и переопределения только части CActiveRecord, сделайте точную копию (за исключением, конечно, свойств) CActiveRecord и внесите в нее изменения.Другими словами, скопируйте даже те методы, которые вы не намереваетесь переопределить.
  2. Выполните изменения, упомянутые выше.Помните, что это включает в себя переопределение getDbConnection и только незначительные правки для дюжины или двух других мест .
  3. Заставьте ваши модели расширять результирующий класс.

Теперь, когда придет время перейти на более позднюю версию Yii, вам необходимо снова синхронизировать ваш класс с CActiveRecord.Запустите ваш любимый инструмент сравнения и сравните ваш класс с целевой версией CActiveRecord.Инструмент сравнения покажет вам только getDbConnection и незначительные правки, плюс любые изменения, внесенные в CActiveRecord в ядре Yii.Скопируйте эти другие изменения в свой класс.Задача решена за 5 минут до вершины.

...