2

我所有的读取都应该转到一个数据库连接 我的所有写入都应该转到另一个连接

如何在 Yii 中实现这一点,而对核心库的代码进行最少的更改?

有时(如评论中所述)我需要能够控制每种模型类型的连接,因此阅读也可以转到大师。

4

1 回答 1

4

我编写了一个应用程序,主管理面板可用于创建和管理多个面向客户的“实例”,因此需要将主应用程序内运行的查询“定向”到任何一个特定于实例的数据库。我将说明一个精简版本,我首先做了什么(这不像你的目标那么苛刻),然后展示一个更强大的方法。

对所有查询使用多个数据库

将查询定向到预先指定的数据库很容易:只需覆盖该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;
    }
}

因此,如果您想将所有操作定向到特定数据库,您只需从而InstanceActiveRecord不是派生 ActiveRecord 模型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再次同步您的课程。启动您最喜欢的 diff 工具并将您的类与目标版本的CActiveRecord. diff 工具将只显示和次要的编辑,以及对Yii 核心getDbConnection所做的任何更改。CActiveRecord将这些其他更改复制到您的班级。问题在 5 分钟内解决。

于 2011-09-14T23:01:17.170 回答