Question

I have a Yii model that will be using (later) multiple databases and the table prefix will be based on a code.

For example:

AMI_tablename, BMI_ AMI_tablename etc

These all tables are same but in different databases.

I want to know how could I provide the dynamic table name to Yii model at run time?

I tried to using a setter function but the parent class CActiveRecord gives an error as it does not get the value from the child model class.

so here is my model code (only the part I have problem)

class RevShareModel extends CActiveRecord
{

    public $prefix;

    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    /**
     * @return string the associated database table name
     */
    public function tableName()
    {
        return $this->prefix . '_revshare_model';
    }

now somewhere in my controller

$obj = RevShareModel::model();
$obj->prefix ="BMI";
$obj->tableName();
$obj->findByPk(1);

exit;

But what I get the error is:

CDbException

The table "_revshare_model" for active record class "RevShareModel" cannot be found in the database.

C:\wamp\www\framework\db\ar\CActiveRecord.php(2264)

seems like when tableName() method is called by CActiveRecord it does not get $prefix.

Was it helpful?

Solution

You've got such error because the table name is actually stored in model's metadata. You can see that by checking contents of $model->getMetaData() which returns CActiveRecordMetaData object. To refresh metadata you should call $model->refreshMetaData() after changing the 'prefix' attribute of your model, e.g.:

...
$obj->prefix ="BMI";
$obj->refreshMetadata();
...

This will definitely do the trick.

OTHER TIPS

You have to override the CActiveRecord::tableName method (probably in an abstract base class for your models). Here's what it does by default:

public function tableName()
{
    return get_class($this);
}

And here's what you could change it to:

abstract class MyActiveRecord extends CActiveRecord
{
    public $prefix; // should probably be private, your call

    public function tableName()
    {
        return $prefix.'_'.get_class($this);
    }
}

This is how I've solved that problem.

private static $_tableName;

public function __construct($tableName)
{
    if(strlen($tableName) == 0)
    {
        return false;
    }

    if(strlen($tableName)>0){
        self::$_tableName = $tableName;
    }

    self::setIsNewRecord(true);
}

public static function model($tableName)
{
    if(strlen($tableName) == 0)
    {
        return false;
    }

    $className=__CLASS__;

    if(strlen($tableName)>0){
        self::$_tableName = $tableName;
    }

    return parent::model($className);
}

public function tableName()
{
    return '{{'.self::$_tableName.'}}';
}

public function setTableName($tableName)
{
    self::$_tableName = $tableName;
}

...

When I use this model, I simply put the name of the table in brackets:

$model = new ClassName($tableName);

I have same problem. I have a model, but I want the data to be saved in different table .So I change the table name before save the data.

public $tbl_name = 'tbl_user_1';//default table
public function tableName()
{
return $this->tbl_name;
}
public function saveDataReg(){
    $mKey = $this->selfMHash($this->username);
    $modKey = $mKey % 2;
    $this->tbl_name = 'tbl_user_' . $modKey;

    $this->refreshMetadata();//change the default Metadata

    $this->save();
}
public function selfMHash($key){
    $md5 = substr(md5($key), 0, 8);
    $seed = 31;
    $hash = 0;

    for($i=0; $i<8; $i++){
        $hash = $hash * $seed + ord($md5{$i});
        $i++;
    }

    return $hash & 0x7FFFFFFF;
}

Now I find the Metadata not be changed when the function of refreshMetadata be used. Last I change the original code

    public function refreshMetaData()
{
    $finder=self::model(get_class($this));
    $finder->_md=new CActiveRecordMetaData($finder);
    if($this!==$finder)
        $this->_md=$finder->_md;

    //return var_dump($this);
}

public function new_refreshMetaData()
{
    $finder=self::model(get_class($this));
    $finder->_md=new CActiveRecordMetaData($this);
    if($this!==$finder)
        $this->_md=$finder->_md;

    //return var_dump($this);
}

I override the function of refreshMetaData and chage the param of CActiveRecordMetaData.

$this->new_refreshMetadata();//use new function

Then it worked. I don't understand the reason of problem.

You can use with MyModel::model()->tableName(), but please don't forget write {{table}}.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top