another_dream
@another_dream
Backend-разработчик, Laravel/ZF2/Yii2

Как правильно организовать внедрение зависимостей в Yii2?

Описал зависимости в конфигурационном файле config/di.php.

Репозитории

<?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);
            },
        ]
    );
}



Сервисы

<?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
                );
            },
        ]
    );
}



Для чего это было реализовано - при построении приложения выделил слой для работы с базой данных через репозиторий и сервисный слой.
В репозитории внедряется AR модель, в свою очередь репозиторий внедряется в сервис (при необходимости).
Бизнес-логику вынес в сервисы. В дальнейшем планирую сервисный слой еще как-то разделить и выделить новые слои.

Укажите, пожалуйста, на ошибки, уверен, что они имеются, так как с DI дело имею сравнительно недавно.
Если есть предложения или критика - буду рад слышать.
  • Вопрос задан
  • 2293 просмотра
Решения вопроса 1
qonand
@qonand
Software Engineer
Вы не совсем правильно понимаете задачи контейнера зависимостей и принципы его работы. Поэтому рекомендую ознакомиться с документацией.
Суть контейнера в том что Вы настраиваете конфигурации компонентов находящихся в нем, а так же зависимости компонентов друг от друга, а уже сам контейнер разбирается как создать объект на основе всех этих данных и со всеми зависимостями. Вы по сути дела только написали лишний код, который реализован в контейнере по умолчанию.

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


Контейнер соответственно сам проинжектит в объекты использующие common\domain\Entities\User\Profile\Interfaces\Repository объект common\infrastructure\Entities\User\Profile\Repository

Сервис регистрируется аналогично:
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')
    ],
);


При создании сервиса, контейнер найден в своих данных объект соответствующий common\domain\Entities\User\Profile\Interfaces\Repository создаст его и проинжектит в сервис
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы