Yii2: Configurable models inside module

2019-02-21 03:09发布

问题:

What is the best practice of including models/activerecords within a Yii2 module in a way that they are configurable?

These are just some of the problems we face when we want to use an activerecord included inside a module:

  1. Adding events & behaviors to models/activerecords provided by a module. I want to attach events and behaviors to the models included in a module using Yii2's configuration format. How can this be done?

  2. Defining relations with models/activerecords that exist outside of the module. When linking an activerecord contained inside a module to the User activerecord we can rely on Ỳii::$app->user->identityClass, but for other custom relations we might need to extend the activerecord. Is there any better approach? Extending activerecord classes from modules somewhat defeats the purpose of modularity.

  3. Configuring various other variables within the module/activerecord. Let's say we want to adjust the max string length validation value. In a module Controller, we can always use $this->module->params to read any custom value, but we cannot do this from a Model or an ActiveRecord. What are we supposed to do instead?

回答1:

I think you might end up using dependency injection:

Write an extension "\common\extensions\MyBootstrap":

namespace common\extensions;

use Yii;
use yii\base\BootstrapInterface;
use yii\base\Application;

class MyBootstrap implements BootstrapInterface {
    /**
     * @param  Application $app Application
     **/
    public function bootstrap($app) {
        Yii::$container->set("common\\modules\\test\\models\\Test1", "common\\modules\\test\\models\\Test2");
    }
}

add to your config:

'bootstrap' => [
    'common\extensions\MyBootstrap',
],

'components' => [ 
    //  ... 
]

and in your code you have to use Yii::$container->get():

$test = Yii::$container->get('common\modules\test\models\Test1');
var_dump($test);

which will create Test2 model instead of Test1. If you want this to happen for your ActiveRecord, override this:

public static function instantiate($row) {
    return \Yii::$container->get(static::class);
}


回答2:

EDIT: The underlying issue has now been resolved. We can use DI to inject relations into ActiveRecords.

As of July 2017, Yii2 does not allow ActiveRecord dependency injection!

See:

  • https://github.com/yiisoft/yii2/issues/8639
  • https://github.com/yiisoft/yii2/issues/11575
  • https://github.com/yiisoft/yii2/issues/5786
  • https://github.com/yiisoft/yii2/pull/14078
  • https://github.com/yiisoft/yii2/issues/13779

The only way around this is to configure your modules through your Yii::$app->params and then use those values inside module ARs (eg. when doing validation).