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:
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?
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.
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?
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);
}
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).