我的所有读取都应该转到一个数据库连接 我的所有写作都应该转到另一个连接
如何在Yii中完成此任务,只需极少更改核心库的代码?
有时(如评论中所述)我需要能够控制每种模型类型的连接,所以读也可以转到Master。
答案 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
同步。
为了移植它并在将来让您的生活更轻松,您可以考虑这种方法:
CActiveRecord
的一部分,而是制作getDbConnection
的精确副本(减去当然的属性)并在那里执行更改。换句话说,复制甚至那些不打算覆盖的方法。CActiveRecord
,只对十几个或其他地方进行非常小的修改。现在,当升级到更高版本的Yii版本时,您需要让您的课程再次与CActiveRecord
同步。启动您最喜欢的差异工具,并将您的班级与getDbConnection
的目标版本进行比较。 diff工具将仅显示CActiveRecord
和次要编辑,以及对Yii核心中{{1}}所做的任何更改。将这些其他更改复制到您的班级。问题在5分钟内解决了。