D
D
Dmitry2017-05-09 18:09:51
Yii
Dmitry, 2017-05-09 18:09:51

How to properly organize dependency injection in Yii2?

Described the dependencies in the configuration file config/di.php .

Repositories

<?php
/**
 * @var $container \yii\di\Container
 */
$container = Yii::$container;

$repositories = [
    # Авторизация - Роли
    'common\domain\Entities\Auth\Role\Interfaces\Repository'             => [
        'concrete' => 'common\infrastructure\Entities\Auth\Role\Repository',
        'model'    => 'common\models\ActiveRecord\AuthRole',
    ],
    # Авторизация - Роли - Назначения
    'common\domain\Entities\Auth\Role\Assignment\Interfaces\Repository'             => [
        'concrete' => 'common\infrastructure\Entities\Auth\Role\Assignment\Repository',
        'model'    => 'common\models\ActiveRecord\AuthAssignment',
    ],
    # Авторизация - Типы персон (формы собственности)
    'common\domain\Entities\Auth\PersonType\Interfaces\Repository'       => [
        'concrete' => 'common\infrastructure\Entities\Auth\PersonType\Repository',
        'model'    => 'common\models\ActiveRecord\AuthPersonType',
    ],


    # Юзер
    'common\domain\Entities\User\Interfaces\Repository'                  => [
        'concrete' => 'common\infrastructure\Entities\User\Repository',
        'model'    => 'common\models\User',
    ],
    # Юзер - Профиль
    'common\domain\Entities\User\Profile\Interfaces\Repository'      => [
        'concrete' => 'common\infrastructure\Entities\User\Profile\Repository',
        'model'    => 'common\models\ActiveRecord\UserProfile',
    ],
    # Юзер - Адреса
    'common\domain\Entities\User\Address\Interfaces\Repository'          => [
        'concrete' => 'common\infrastructure\Entities\User\Address\Repository',
        'model'    => 'common\models\ActiveRecord\UserAddress',
    ],
];

foreach ($repositories as $abstract => $realisation) {
    $model = $realisation['model'];
    $concrete = $realisation['concrete'];
    $container->setSingletons(
        [
            // С абстракции (интерфейса) на реализацию
            $abstract => $concrete,
            // Инъекция модели в реализацию
            $concrete => function () use ($concrete, $model) {
                return new $concrete(new $model);
            },
        ]
    );
}


Services

<?php
/**
 * @var $container \yii\di\Container
 */
$container = Yii::$container;

$services = [
    # Авторизация - Безопасность
    'common\domain\Entities\Auth\Security\Interfaces\Service'         => [
        'concrete' => 'common\application\Auth\Security\Service',
        'args'     => [],
    ],
    # Авторизация - Роли
    'common\domain\Entities\Auth\Role\Interfaces\Service'             => [
        'concrete' => 'common\application\Auth\Role\Service',
        'args'     => [
            'common\domain\Entities\Auth\Role\Interfaces\Repository',
            'common\domain\Entities\Auth\Role\Assignment\Interfaces\Repository'
        ],
    ],
    # Авторизация - Типы персон (формы собственности)
    'common\domain\Entities\Auth\PersonType\Interfaces\Service'       => [
        'concrete' => 'common\application\Auth\PersonType\Service',
        'args'     => [
            'common\domain\Entities\Auth\PersonType\Interfaces\Repository',
        ],
    ],


    # Юзер
    'common\domain\Entities\User\Interfaces\Service'                  => [
        'concrete' => 'common\application\User\Service',
        'args'     => [
            'common\domain\Entities\User\Interfaces\Repository',
            'common\domain\Entities\Auth\Security\Interfaces\Service',
            'common\domain\Entities\Auth\Role\Interfaces\Service',
            'common\domain\Entities\User\Profile\Interfaces\Service'
        ],
    ],
    # Юзер - Профиль
    'common\domain\Entities\User\Profile\Interfaces\Service'      => [
        'concrete' => 'common\application\User\Profile\Service',
        'args'     => [
            'common\domain\Entities\User\Profile\Interfaces\Repository',
        ],
    ],
    # Юзер - Адреса
    'common\domain\Entities\User\Address\Interfaces\Service'          => [
        'concrete' => 'common\application\User\Address\Service',
        'args'     => [
            'common\domain\Entities\User\Address\Interfaces\Repository',
        ],
    ],
];

foreach ($services as $abstract => $realisation) {
    $arguments = $realisation['args'] ?? null;
    $concrete = $realisation['concrete'];
    $container->setSingletons(
        [
            // С абстракции (интерфейса) на реализацию
            $abstract => $concrete,
            // Инъекция зависимостей в реализацию сервиса
            $concrete => function () use ($container, $concrete, $arguments) {
                $dependencies = array_map(
                    function ($item) use ($container) {
                        return $container->get($item);
                    },
                    $arguments
                );

                return new $concrete(
                    ...$dependencies
                );
            },
        ]
    );
}


Why this was implemented - when building the application, I allocated a layer for working with the database through the repository and the service layer.
The AR model is implemented in the repository, in turn, the repository is embedded in the service (if necessary).
Moved the business logic into services. In the future, I plan to somehow separate the service layer and select new layers.
Please point out the errors, I'm sure that they exist, since I have dealt with DI relatively recently.
If you have any suggestions or criticism - I will be glad to hear.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Maxim Fedorov, 2017-05-10
@another_dream

You do not quite correctly understand the tasks of the dependency container and how it works. Therefore, I recommend that you read the documentation .
The essence of the container is that you configure the configurations of the components located in it, as well as the dependencies of the components from each other, and the container itself already understands how to create an object based on all this data and with all the dependencies. You essentially only wrote extra code that is implemented in the default container.
Take for example your repository object, it is enough to register it in the container like this

Yii::$container->setSingleton('common\domain\Entities\User\Profile\Interfaces\Repository', // указываем интерфейс
    [ // указываем конфигурацию класса реализующего этот интерфейс
        'class' => 'common\infrastructure\Entities\User\Profile\Repository' 
    ], 
    [ // указываем какие данные необходимо передать в конструктор, в частности - экземпляр класса UserProfile
        Instance::of('common\models\ActiveRecord\UserProfile')
    ]
);

The container accordingly injects into objects using common\domain\Entities\User\Profile\Interfaces\Repository the common\infrastructure\Entities\User\Profile\Repository object
The service is registered in the same way:
Yii::$container->setSingleton('common\domain\Entities\User\Profile\Interfaces\Service',
    [
        'class' => 'common\application\User\Profile\Service',
    ],
    [
        Instance::of('common\domain\Entities\User\Profile\Interfaces\Repository')
    ],
);

When creating a service, the container found in its data an object corresponding to common\domain\Entities\User\Profile\Interfaces\Repository will create it and inject into the service

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question