How to set ActiveRecord table name dynamicly

For instance:

$ar = new ArTable();

$ar->setTable(‘table_abc’);

$ar->find();

My problem is:

Follw Yii ActiveRecord Design, table name must be set when constructed, especially for ::model() static method, that’s losting lost of flexibility.

Can you tell us, please, some more details?

Looking for something like this?




public function actionSomething ($table){

   $ar = new ArTable();

   $ar->setTable($table);

   $ar->find();			

}



Open:

/project/?r=controller/something&table=users

and that will find everything from table users. Also, you can set default table by:




public function actionSomething ($table='some_table')



I hope that is what you are looking for.

Hi, dstudio, It’s about database sharding:

$ar = new ArTable();

This setTable(assumed) method:

$ar->setTable(‘shardding_table_1’);

$ar->setTable(‘shardding_table_2’);

will allow you to choose different sharding tables(set specific table name) after ArTable constructed.

Or can we assign table name to ArTable when it’s constructed like:

$ar1 = new ArTable(‘shardding_table_1’);

$ar2 = new ArTable(‘shardding_table_2’);

That would be nice too, It’s not realistic to creat every class file according to every shard_table which have the same database structure.

Or there is other way on current yii version? To set table name when ar class is initiating.


public function tableName(){}

Won’t work. Because getting a sharding table name must have a sharding key passed into the tableName() function, unless we introduce an external dependency in tableName():







class ArTable extends CActiveRecord{

 public $sharding_table;


 public function tableName(){

    if(!isset($this->sharding_table)){

       global $sharding_table;

       $this->sharding_table = $sharding_table;

    }

    return $this->sharding_table;

 }

}


The execution flow is(Just prototyping):


$sharding_table = 'table_1';


$ar1 = new ArTable();


..then $ar1 is with table_1


$sharding_table = 'table_2';


$ar2 = new ArTable();


..then $ar2 is with table_2







Though it’s workable but not secure

Can you normalize the database table and use single table inheritance?

http://www.yiiframework.com/wiki/198/single-table-inheritance/

That still need to create specific classes for different shards in my point:(

In zend framework docs, I saw it can to this:

"The schema and table names may also be specified via constructor configuration directives, which override any default values specified with the $_name and $_schema properties. A schema specification given with the name directive overrides any value provided with the schema option."

http://framework.zend.com/manual/en/zend.db.table.html

which give flexibilities.

Why should it be in such a strange manner? You don’t need globals. Next code seem to be similar to what You need:


/**

 * @property string $shard Shard defining suffix to be added to table name.

 */

class ShardedActiveRecord extends CActiveRecord

{

    private $_shard;


    public function tableName(){

        return get_class($this) . '_' . $this->shard;

    }


    public function setShard($shard) {

        $this->_shard = $shard;

        return $this;

    }


    public function getShard() {

        //Just to have meaningful default behavior

        if($this->_shard === null){

            $this->_shard = 1;

        }

        return $this->_shard;

    }

}




class Model extends ShardedActiveRecord{

    /**

     * @static

     * @return Model

     */

    public static function model(){

        return parent::model(__CLASS__);

    }

}


$recordsFromShardOne = Model::model()->setShard(1)->findAll();


$model = new Model();

$model->shard = 2;


//do other stuff

The point is:

IN THE Process OF CActiveRecord initiation, the __construct() or ::model() method will call tableName() immediately to read the table meta info , which give no chance to call setShard($shard), you will have to

First:

$ar = new ShardedActiveRecord(); <- In this inside __construct() tableName() will be called before setShard($shard) take place

Then:

$ar->setShard($shard);

================

I not sure if set table name after new ShardedActiveRecord() will work, It’s not tested yet.

I looked through the code in different places, and I think it should work with such an addition


/**

 * @property string $shard Shard defining suffix to be added to table name.

 */

class ShardedActiveRecord extends CActiveRecord

{

    private $_shard;


    public function tableName(){

        return get_class($this) . '_' . $this->getShard();

    }


    public function setShard($shard) {

        $this->_shard = $shard;

        call_user_func(array(get_class($this), 'model'))->_shard = $shard;

        $this->refreshMetaData();

        return $this;

    }


    public function getShard() {

        //Just to have meaningful default behavior

        if($this->_shard === null){

            $this->setShard(1);

        }

        return $this->_shard;

    }

}

You may also play with getMetaData() and getTableSchema() to make it return different values depending on current tableName() value;

I’ve edited code above after some testing. Now it seems to work. :)

Thanks Yurko, I’ll try it out :)

i got the same problem here my code




class AutoPay extends CActiveRecord

{

	public $tname = '';


	public function __construct($tname = '',$scenario='insert')

	{

		

		$this->tname = $tname;

		

		parent::__construct($scenario='insert');

		

	}

	/**

	 * Returns the static model of the specified AR class.

	 * @param string $className active record class name.

	 * @return Pay the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		echo $this->tname;exit;

		return '{{'.$this->tname.'}}';

	}



error:Fatal error: Maximum function nesting level of ‘512’ reached, aborting!

I have reached your own conclusion,

and the same error :slight_smile:





	private $tab = 'experiments';

/*	

	public function __construct($scenario) {

		$this->tab = 'well_it_works';		

		parent::__construct($scenario); // chiamo il costruttore padre

	}	

*/

	

	public function __construct($table = 'table_test',$scenario='search')

	{

		$this->tab = $table;

		parent::__construct($scenario); // chiamo il costruttore padre

	}



Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 261900 bytes) in /var/www/yii-1.1.9.r3527/framework/db/ar/CActiveRecord.php on line 48