I created a “shard table manager” to be able to use my models with a dynamic name. The thing is, when I need to create a relation I have a problem, I can’t dynamically add the table extension to my relation. Here is the code I am using and that is not working:
class StatReport extends ShardedActiveRecord{
...
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'StatInfos' => array(self::HAS_MANY, 'StatInfo'.$this->getShard(), 'report_id'),
);
}
}
It is not working because the model expect the file StatInfo<shard>.php to exist… Is there a way to dynamically allocate a relation with a reference to the StatInfo.php model page?
I believe its possible but you probably have to digg into Yii core and figure out how relationships are implemented its a interesting problem I am trying to reproduce this might take me sometime
I have been thinking about this and I came up with a solution that should work! I had to reimplement the CActiveFinder and the CActiveRecord classes. I am not sure if everything I did is clean so here is the code, feel free to comment!
The ShardedActiveRecord class (Reimplementation of CActiveRecord):
class ShardedActiveRecord extends CActiveRecord{
private $_shard;
public function tableName(){
return get_class($this) . $this->getShard();
}
public function setShard($shard) {
$connection=Yii::app()->db;
$command = $connection->createCommand("SHOW TABLES LIKE '".get_class($this).$shard."'");
if(!$command->execute())
throw new CHttpException(404,'The specified table cannot be found.');
$this->_shard = $shard;
call_user_func(array(get_class($this), 'model'))->_shard = $shard;
$this->refreshMetaData();
return $this;
}
public function getShard() {
if($this->_shard === null){
$this->setShard("");
}
return $this->_shard;
}
public function createShardedTable($shard){
$connection=Yii::app()->db;
$command = $connection->createCommand("CREATE TABLE IF NOT EXISTS ".get_class($this).$shard." LIKE ".get_class($this));
$command->execute();
}
public function beforeSave(){
return parent::beforeSave();
}
public function getMergedDataProvider($params, $criteria = null){
$modelArray = array();
$model = array();
foreach ($params as $param){
$tmpmodel = $this->model()->setShard($param);
if($criteria === null){
$data = $tmpmodel->findAll();
}else{
$data = $tmpmodel->findAll($criteria);
}
if(count($data) != 0){
array_push($modelArray, $data);
}
}
foreach($modelArray as $ma){
$model = array_merge($model, $ma);
}
return $model;
}
public function getActiveFinder($with)
{
return new ShardedActiveFinder($this,$with);
}
}
The ShardedActiveFinder class (Reimplementation of CActiveFinder):
class ShardedActiveFinder extends CActiveFinder{
private $_shard;
public function __construct($model,$with)
{
$this->_shard=$model->getShard();
parent::__construct($model,$with);
}
public function getModel($className)
{
return ShardedActiveRecord::model($className)->setShard($this->_shard);
}
}
Some feedback would be really appreciated! Thank you.