DI для ServiceLocator

Обсуждение документации второй версии фреймворка. Переводы Cookbook и авторские рецепты.
Ответить
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

DI для ServiceLocator

Сообщение ElisDN »

Часто возникает мысль в один компонент передать другой, как это реализовано в сервисах того же Symfony2. В итоге в Yii2 приходится костылить, передавая строкой имя компонента:

Код: Выделить всё

'components' => [
    'storage' => [
        'class' => 'app\components\Storage',
    ],
    'component' => [
        'class' => 'app\components\Component',
        'storage' => 'storage',
    ],
], 
или, что неудобнее, городить вложенный конфиг:

Код: Выделить всё

'components' => [
    'component' => [
        'class' => 'app\components\Component',
        'storage' => [
            'class' => 'app\components\Storage',
        ],
    ],
], 
чтобы потом вместо простого использования:

Код: Выделить всё

class Component
{
    public $storage;
} 
извращаться с получением компонента из строки по переданному имени:

Код: Выделить всё

class Component
{
    private $_storage;

    public function setStorage($value)
    {
        $this->_storage = Yii::$app->get($value);
    }
} 
или создавать из переданного массива:

Код: Выделить всё

class Component
{
    private $_storage;

    public function setStorage($value)
    {
        $this->_storage = Yii::createObject($value);
    }
} 
что, к слову, снаружи выглядит неочевидно и засоряет классы.

Было бы неполохо передавать компоненты неким маркером отложенного связывания по аналогии с DI для классов:

Код: Выделить всё

'components' => [
    'storage' => [ ... ],
    'component' => [
        'class' => 'app\components\Component',
        'storage' => Instance::of('storage'),
    ],
], 
Но DI в Yii2 работает только с привязкой к классам через контейнер, поэтому недоступно в ServiceLocator для компонентов.

Чьё-то обсуждение закрылось весьма оперативно. Но, к костыльному счастью, можно определять компоненты анонимками. Так что на данный момент нашёл более-менее вменяемый вариант побороть это неудобство:

Код: Выделить всё

'components' => [
    'storage' => [ ... ],
    'component' => function () { return Yii::createObject([
        'class' => 'app\components\Component',
        'storage' => Yii::$app->get('storage'),
    ]); },
], 
Ответить