我有 Yii 应用程序和两个结构相同的表 tbl 和 tbl_history :
现在想要创建模型,因此它将在调用模型时通过我发送的参数选择表。例如:
MyModel::model('tbl')->find();
//and
MyModel::model('tbl_history')->find();
在Yii论坛中找到解决方案related article。做了相同的更改,最后在 MyModel :
中得到了这个private $tableName = 'tbl'; // <=default value
private static $_models=array();
private $_md;
public static function model($tableName = false, $className=__CLASS__)
{
if($tableName === null) $className=null; // this string will save internal CActiveRecord functionality
if(!$tableName)
return parent::model($className);
if(isset(self::$_models[$tableName.$className]))
return self::$_models[$tableName.$className];
else
{
$model=self::$_models[$tableName.$className]=new $className(null);
$model->tableName = $tableName;
$model->_md=new CActiveRecordMetaData($model);
$model->attachBehaviors($model->behaviors());
return $model;
}
}
现在我做的时候:
echo MyModel::model('tbl_history')->tableName(); // Output: tbl_history
它返回正确的值,但是:
MyModel::model('tbl_history')->find();
仍会返回 tbl 的值。
添加了:
public function __construct($id=null,$scenario=null){
var_dump($id);
echo '<br/>';
parent::__construct($scenario);
}
得到了:
string(tbl_history)
string(tbl_history)
NULL
这意味着 Yii 从其他地方调用模型,但不知道从哪里以及如何防止它。
它也会对模型进行2次调用,这对性能来说太糟糕了吗?
答案 0 :(得分:5)
看起来需要覆盖CActiveRecord::getMetaData()
方法才能实现您的目标。
<?php
class TestActiveRecord extends CActiveRecord
{
private $tableName = 'tbl'; // <=default value
private static $_models=array();
private $_md;
public function __construct($scenario='insert', $tableName = null)
{
if($this->tableName === 'tbl' && $tableName !== null)
$this->tableName = $tableName;
parent::__construct($scenario);
}
public static function model($tableName = false, $className=__CLASS__)
{
if($tableName === null) $className=null; // this string will save internal CActiveRecord functionality
if(!$tableName)
return parent::model($className);
if(isset(self::$_models[$tableName.$className]))
return self::$_models[$tableName.$className];
else
{
$model=self::$_models[$tableName.$className]=new $className(null);
$model->tableName = $tableName;
$model->_md=new CActiveRecordMetaData($model);
$model->attachBehaviors($model->behaviors());
return $model;
}
}
public function tableName()
{
return $this->tableName;
}
/**
* Returns the meta-data for this AR
* @return CActiveRecordMetaData the meta for this AR class.
*/
public function getMetaData()
{
if($this->_md!==null)
return $this->_md;
else
return $this->_md=static::model($this->tableName())->_md;
}
public function refreshMetaData()
{
$finder=static::model($this->tableName());
$finder->_md=new CActiveRecordMetaData($finder);
if($this!==$finder)
$this->_md=$finder->_md;
}
}
答案 1 :(得分:2)
也许更容易制作MyModelHistory,它扩展MyModel并只覆盖一个方法 - tableName()。
答案 2 :(得分:2)
我建议实现单表继承。为此,您需要将表与标志或类型列组合在一起,以指出这是否是历史记录。我在底部粘贴了一些链接,因此您可以看到它在Yii中是如何实现的,并列出了下面的一些好处。
优势:
您无需复制模型之间常用的代码
此表的更改只需执行一次。
只需要对父模型进行一次更改。
代码通常更易于维护和阅读。
您将专门针对tbl
和tbl_history
http://www.yiiframework.com/wiki/198/single-table-inheritance/ http://en.wikipedia.org/wiki/Single_Table_Inheritance
答案 3 :(得分:2)
我在几个月前创建了一个执行此操作的解决方案。这是一个完全动态的解决方案,您只需将表名称传递给模型即可。该解决方案最初设计用于跨多个数据库使用相同的数据库结构,但是使其适应在同一数据库中工作是微不足道的。该文档是here。我建议阅读它,因为它有更多关于CDynamicRecord的详细信息
很容易适应多个表的工作。您可以将改编作为要点下载from github。
1)下载Gist并将其放入ext,另存为CDynamicRecordSDB.php
2)在模型中创建模型,并按如下方式进行设置:
基本上,你想扩展CDynamicRecord,并覆盖你的model()和tableName(),使它们符合CDyanmicRecord。
<?php
Yii::import('ext.CDynamicRecordSDB');
class Test extends CDynamicRecordSDB
{
public static function model($dbConnectionString = 0, $className=__CLASS__)
{
return parent::model($dbConnectionString, $className);
}
public function tableName()
{
return $this->dbConnectionString;
}
[... Do everything else after this ...]
}
3)像往常一样设置模型。
用法与CActiveRecord相同,您可以执行所有操作。没有惊喜。下面只是几个例子。
$data = Test::model('tbl')->findAll();
$data2 = new Test('tbl');
$data2->findAll();
foreach ($data as $row)
print_r($row->attributes);
$data = Test::model('tbl_history')->findAll();
foreach ($data as $row)
print_r($row->attributes);
执行此操作的唯一限制是您必须修改关系的工作方式。如果您计划访问相关模型(Bar),并且您无意单独调用Bar。然后Bar应该扩展CActiveRecord,在Foo中你可以定义正常的关系。 Yii神奇地为你跨越实例传递CDbConnectionString。
另外,如果您打算在同一个数据库中访问模型,但又希望保留自己调用它们的能力,那么Bar应该扩展CDynamicModel,而Foo应该有一个如下定义的getter。
public function getBar()
{
return Bar::model($this->$dbConnectionString);
}
答案 4 :(得分:1)
一个小小的方法,但为我工作任意数量的表
public static $dynamic_table_name="main_table";
public static function setDynamicTable($param)
{
self::$dynamic_table_name=self::$dynamic_table_name.$param;
}
/**
* @return string the associated database table name
*/
public function tableName($param='')
{
self::setDynamicTable($param);
return self::$dynamic_table_name;
}
// to use it like
ModelName::model()->tableName('_one');
ModelName::model()->tableName('_two');
ModelName::model()->tableName('_three');