Yii 的 CActiveRecord 是不支持数据库分表的,并且采用 className <—-> 数据表 一一对应的方式,所以你很难通过动态修改表名来实现分表。
我们这里的原则是,不动 Yii 任何现有的代码(为了以后跟随 Yii 升级的考虑),所以只能在我们自己的程序里面下功夫。
所谓分表,其实就是有一组 结构一模一样的表,它们唯一的区别就是表的名字不一样。所以我们这里想到了设计模式中的 Factory 模式。
下面是我们的实现,假如有一个表 article_content 结构,物理上有多个结构一样的表,名字为 article_content0, article_content1, article_content2, ….
我们首先用 Gii 模块自动生成 article_content 这个 Model ,如下:
基本的 article_content ModelPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ArticleContent extends CActiveRecord
{
/**
* Returns the static model of the specified AR class.
* @param string $className active record class name.
* @return ArticleContent the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* @return string the associated database table name
*/
public function tableName()
{
return '{{article_content}}';
}
// 后面代码省略
接下来,我们有几个物理分表,就建立几个 Model (注意这里的 Model 继承于 ArticleContent,只是简单的重载了 tableName 方法,其它都不需要实现)
ArticleContent0.php 文件PHP
1
2
3
4
5
6
7
8
class ArticleContent0 extends ArticleContent
{
public function tableName()
{
return '{{article_content0}}';
}
}
ArticleContent1.php 文件PHP
1
2
3
4
5
6
7
8
class ArticleContent1 extends ArticleContent
{
public function tableName()
{
return '{{article_content1}}';
}
}
注意我们这里的命名规范, 数据表名为 article_content1 对应的 class 和文件名为 ArticleContent1 ,通过这个命名规范我们就可以利用 Factory 模式来实现分表访问。
下面是我们设计的 Factory 模式,自动根据你的参数 来生成对应的 Model
Factory 模式实现自动根据表名建立对应的 ModelPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class PartitionTableFactory {
public static function getTableModel($tableName){
if (empty($tableName)){
return null;
}
// construct class name
$itemArray = explode('_',$tableName);
if(empty($itemArray)){
return null;
}
// capitalize the first character
$className = '';
foreach ($itemArray as $item){
$className .= ucfirst($item);
}
return new $className;
}
}
有了 Factory 模式,我们编程使用就简单了,使用方法如下:
使用分表的方式PHP
1
2
3
4
5
6
7
8
9
10
// 这里根据上一级的参数决定应该把数据放到哪个分表中,用 Factory 自动生成对应分表的 Model
$articleContent = PartitionTableFactory::getTableModel($articleTitle->content_table_name);
$articleContent->title_id = $articleTitle->id;
$articleContent->sequence_id = $articleTitle->content_count;
$articleContent->url = $pipelineContext->fetchPageUrl;
$articleContent->content = $authorArticleItem['content'];
$articleContent->hash = $hashValue;
$articleContent->save();
这里的实现方法并不完美,毕竟每多一个物理分表,就需要多建一个 ArticleContent(N) 的 model,但是这个方法目前使用起来还算简单,同时不需要对 Yii 做任何修改,方便日后升级 Yii 框架。
要完美的方法,只能希望将来某天 Yii 能够假如对 分库分表 的实现 ,那我们就不用这么麻烦了。