Dynamic table names in Yii2

2019-02-18 15:25发布

问题:

I have a Yii2 Model that uses multiple tables with the same structure. The table names will change according to the user logged in and the table names are very unique and depends on the username .How will i assign this table name to the model dynamically?I have done this so far.

In my model:

    protected $table;

    public function __construct($table)
    {
    $this->table='custom_risk_assignment_table';//logic to obtain the table name goes here
    }

    public static function tableName()
    {
    return $this->table;
    }

But by doing so results in error Using $this when not in object context since function tableName() is a static function.

How can i do it then?Any help appreciated.Thanks in advance!

Edit:A detailed picture

Say i have a user from the company ABC. I have so many processes in my application,say PQR is one of them. If user from the company ABC logins and choose the process PQR,i need to create a table ABC_PQR in my database if not exists or load the table if it is already created. I need this table name into my model. Similarly may users and many processes are there.What will be the best approach to manage the database.

回答1:

Since tableName is a static method (exactly what the error message says) you don't have access to non-static properties. (you can't use $this keyword)

So you'd have to declare a static property:

protected static $table;

public function __construct($table)
{
    self::$table = $table;
}

public static function tableName()
{
    return self::$table;
}

/* UPDATE */
public static function setTableName($table)
{
    self::$table = $table;
}

UPDATE:

Ok my fault. The constructor will not get called if you call a static method like updateAll, find etc. And $table will not get set. So you have different options.

  1. Manually call the constructor before using the static db manipulation methods.
  2. Add a static setter to the model like public static function setTableName($tableName) and call it on every successful login.


回答2:

One way to get out of this situation without hacks is to not vary the return value of tableName but instead use different classes for the different tables. Those classes would differ only in the implementation of tableName:

abstract class Foo extends ActiveRecord
{
    // your existing code goes here

    abstract function tableName();
}

class FooOne extends Foo
{
    function tableName() { return 'table1'; }
}

class FooTwo extends Foo
{
    function tableName() { return 'table2'; }
}

Then, at some appropriate place in your app you would decide which table you want to use and remember what the model for that table is. Something like:

if ($username == "anne") {
    $fooModel = new FooOne();
}
else if ($username == "bob") {
    $fooModel = new FooTwo();
}

After this you can simply use $fooModel as the call target and the query will affect the appropriate table automatically. For example:

$foos = $fooModel::findAll();


回答3:

This problem has been going on for a long time, but I just saw this problem and have some ideas, I hope to help later people.

Setting the table name dynamically through the constructor is not friendly. we can bind the table name by setting a factory method, for example:

class MyTableModel extends \yii\db\ActiveRecord {

    protected static $table;

    public static function useTable($table) {
        static::$table = $table;

        return new static();
    }

    public static function tableName() {
        return static::$table;
    }
}

When used, bind the table name through the useTable, for example:

// find
MyTableModel::useTable('tableName1')::find()->where(...)->all(); // use tableName1
MyTableModel::useTable('tableName1')::findAll(); // use tableName1
MyTableModel::useTable('tableName1')::updateAll(); // use tableName1

MyTableModel::useTable('tableName2')::findAll(); // use tableName2

// create 
$table = MyTableModel::useTable('tableName2');
$table->attributes = $attributes;
$table->save()

Use the useTable method to achieve a fully bound target.

Note that this is not done with a closure, but instead sets the value of the MyTableModel::$table static variable. So calling useTable multiple times will use the last set value



标签: php oop yii2