Adding the feature to resolve dependencies automatically from the configuration

Hi, Yii enthusiasts. Recently I presented a proof of concept that would allow Yii container to resolve automatically dependencies based on their configuration. I’m mentioning it here because I would feel sorry if it didn’t get enough attention it deserves (at least in my opinion).

Take a look here https://github.com/yiisoft/yii2/pull/18415 and share your thoughts - should it be added or not; does it need any changes; how the method should be renamed or should there be a new one…

The concept is as following. Right now you can create object based on its configuration using Yii::createObject() or directly with yii\di\Container::get(). Something like this is available:

Yii::createObject(MyClass::class);
// equivalent of
Yii::$container->get(MyClass::class);

which creates new instance of MyClass. You can as well use configuration array like:

Yii::createObject([
    'class' => MyClass::class,
    'property' => 'value',
]);

which additionally sets a property with new value.

Dependency Injection Container is explained in the Guide and you can find there few examples of defining dependencies (see Practical Usage) that allows to create objects that depend on them. Let’s take an example:

class Main
{
    private DepInterface $dependency;

    public function __construct(DepInterface $dependency)
    {
        $this->dependency = $dependency;
    }
}

So that class requires an instance of object implementing DepInterface to be passed to its constructor. When we want to use it with Yii’s Container something like Yii::createObject(Main::class) is not enough - there will be an error that $dependency is missing. createObject() has second argument - $params - exactly for constructor parameters. But to use it now we would have to do something like:

$dependencyInstance = new Dependency(); // Dependency implements DepInterface
Yii::createObject(Main::class, ['dependency' => $dependencyInstance]);

Or we would have to prepare Yii’s Container for that dependency with:

Yii::$container->set(DepInterface::class, Dependency::class);
// and now the below works
Yii::createObject(Main::class); // $dependency is already set

But this is not possible currently:

Yii::createObject(Main::class, ['dependency' => Dependency::class]);

So I’m proposing to allow it (hence the PR). Currently such configuration will end with error stating that you are trying to pass string (class name) when the constructor expects object. I’m taking advantage here of the fact that Yii is detecting that constructor requires object so anything that is passed there as that very dependency can be a subject of autoresolving (just like it would be an argument of its own Yii::createObject() call).

Such change in my opinion would help writing less code and preparing cleaner application configurations. Let us know what you think.

2 Likes