如何在Yii中根据查询类型使用不同的连接

时间:2011-09-14 21:17:48

标签: php dbconnection yii

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

如何在Yii中完成此任务,只需极少更改核心库的代码?

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

1 个答案:

答案 0 :(得分: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而不是CActiveRecord派生您的ActiveRecord模型,然后只需执行InstanceActiveRecord::dbConnection = $connection即可。去。

使用基于查询类型自动选择的多个数据库

为此你需要深入CActiveRecord。事实证明,getDbConnection主要由getCommandBuilder使用,而{{3}}又是所有delete / update / insert系列调用的方法。因此,我们需要将某些上下文从这些函数传递到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(); } 1}}在我看的时候)并覆盖它们以适当地指定读/写上下文。例如:

true

在此之后你应该准备好了。除了这里所示的false / CActiveRecord选项之外,没有什么可以阻止你做出更复杂的上下文选择机制,概念是相同的。

实际问题

虽然所有这些都将完美地实现既定目标,但仍然存在关于这种方法的可维护性的问题。

这条路线确实会涉及来自CActiveRecord的大量复制/粘贴代码,如果有机会稍后将您的应用移动到更高版本的框架,这是不理想的;为此,您将被迫使您的子类与最新版本的CActiveRecord同步。

为了移植它并在将来让您的生活更轻松,您可以考虑这种方法:

  1. 而不是复制/粘贴并仅覆盖CActiveRecord的一部分,而是制作getDbConnection的精确副本(减去当然的属性)并在那里执行更改。换句话说,复制甚至那些打算覆盖的方法。
  2. 执行上述更改。请注意,这涉及覆盖CActiveRecord ,只对十几个或其他地方进行非常小的修改
  3. 让您的模型扩展生成的类。
  4. 现在,当升级到更高版本的Yii版本时,您需要让您的课程再次与CActiveRecord同步。启动您最喜欢的差异工具,并将您的班级与getDbConnection的目标版本进行比较。 diff工具将仅显示CActiveRecord和次要编辑,以及对Yii核心中{{1}}所做的任何更改。将这些其他更改复制到您的班级。问题在5分钟内解决了。