ppokrovsky
@ppokrovsky

Подход к реализации DI в проектах Yii2 — правильно или не очень?

Привет.

UPD: просто оставлю это здесь
stackoverflow.com/questions/24381708/default-value...

В своих проектах на Yii2 я достаточно активно использую следующую конструкцию:
use app\models\Dependency;

class Model extends yii\base\Model
{
    public $dependencyClass;
    public function __construct($config = [])
    {
        if (!isset($config['dependencyClass'])) {
            $this->dependencyClass = new Dependency();
        }
        parent::__construct($config);
    }
}


тут Model это модель, а Dependency это какая-то зависимость, например ассоциированный класс.

Дальше в классе, если нужно использовать зависимость, то обращаюсь к $this->dependencyClass, например:
public function getDependencies()
{
    return $this->hasMany($this->dependencyClass->className(), ['modelId' => 'id']);
}


Привычка использовать такую конструкцию появилась достаточно давно и в целом идея была в том, чтобы:
а) собирать все зависимости в одном месте (конструктор класса), а не раскидывать вызовы по всему классу;
б) иметь "дефолтную" зависимость, которая бы подключалась если зависимость не передана в конструктор явно;
в) иметь возможность заинджектить зависимость, что здорово помогает например в юнит-тестах (там я делаю зависимости моками).

Вот просто хотел спросить совета, возможно в такой реализации есть какие-то антипаттерны и вообще запашок и перечисленные подходы можно реализовать как-то более по-другому?

Спасибо.
  • Вопрос задан
  • 960 просмотров
Пригласить эксперта
Ответы на вопрос 2
@MadridianFox
Web-программист, многостаночник
Какое-то однобокое решение:
if (!isset($config['dependencyClass'])) {
    $this->dependencyClass = new Dependency();
}

Вроде как и внедрение зависимости, но всё-равно остаётся жесткая связь.

А чем вас не устроил родной Yii сервис-локатор через конфиг?
Можно было бы делать конфигурирование зависимости как компонента yii вот так:
//config/web.php
"components"=>[
    //...
    "model_deps"=>[
          "class"=>"app\components\ModelDependenciesService",
          "default_dependency"=>"app\model\model_2",
          "dependencies"=>[
                 "app\model\model_1"=>[
                        "dep_1"=>"app\model\model_3",
                        //....
                  ]
           ]
     ]
    //...
]
// в базовом классе модели
public function getDepClass($dep_name){
    retrn Yii::$app->model_deps->depClass(static,$dep_name); // не помню точно как текущий класс брать
}
// где-то в модели
public function getDependencies()
{
    return $this->hasMany($this->getDepClass($dep_name), ['modelId' => 'id']);
}

В классе ModelDependenciesService, естественно надо реализовать метод depClass, который будет возвращать класс зависимости или класс по умолчанию, если зависимость не определена.
Ответ написан
Комментировать
Insolita
@Insolita
Отчаянная домохозяйка
А чем классический вариант не устраивает?

Объявите интерфейс который нужен зависимой модели и прямо в конструктор его, можно от ActiveRecordInterface унаследовать
Пусть ваша модель будет Post с Зависимостью Comments

interface PostCommentModelInterface // можно extends ActiveRecordInterface
     {
        /**
         * @return ActiveRecord[]|Comment[]
        **/
            public function findForPost();

    }

     class Model extends yii\base\Model
     {
           protected $commentModel;
           public function __construct(PostCommentModelInterface $commentModel, $config = [])
          {
                $this->commentModel = $commentModel;
                parent::__construct($config);
           }
    ]

А зависимости региструются в начале приложения после запуска конфига, в файле bootstrap.php
типа Yii::$container->set('\app\modules\posts\PostCommentModelInterface','\app\modules\comments\CommenModel')

только создавать модель надо будет не new Post(), а Yii::createObject(['class'=>'\app\module\Post']) тогда автоматом зависимость подключит... ну или вручную new Post(new Comment())

ну или альтернативный, но менее красивый вариант

...
     protected $commentModel;

      public function __construct(\yii\container\Container $container, $config = [])
      {
         $this->commentModel = $container->get('\app\modules\posts\PostCommentModelInterface'');
          parent::__construct($config);
       }
   ...


правда смущает ваше $this->hasMany ... при extends yii\base\Model ...
посмотрите тут про паттерны, особенно в комментах
https://habrahabr.ru/post/183658/#comment_6392902
Ответ написан
Ваш ответ на вопрос

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

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