Service Locator Repository injection with trait

fixxxer

К.О.
Партнер клуба
@grigori, если sql снаружи, проблема отпадает сама по себе, т.к. инжектить не надо ничего вообще.

С банальным table gateway конечно все просто, тут и примеры не нужны, ты покажи развесистый aggregate root с кучей логики и с маппингом на пачку таблиц. Я вот не знаю, как ты это сделаешь без публичных атрибутов, только если reflection-ом обмажешься... получится в итоге первая доктрина :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@grigori, если sql снаружи, проблема отпадает сама по себе, т.к. инжектить не надо ничего вообще.
То есть ты генеришь SQL в одном объекте, а выполняешь в другом, и вызываешь все это в третьем, в котором уже есть соединение с базой. В третий все-равно надо инъектить :)
 

fixxxer

К.О.
Партнер клуба
Ну так то маппер, а мапперы - они через фабрику.
Если не маппер, а какая-нибудь выбиралка статсов - через конструктор.
В entities не инжектится.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
объект получается мертво прибит по имени к этому трейту. Тогда как в случае контейнеров тебе ничего не мешает создать два разных контейнера с разными зависимостями и создать два объекта одного класса через два разных контейнера.
Можно задать трейту другой конфиг разрешения зависимостей, и будет то же самое.
Приведи пример, когда тебе нужно два разных DI в одном приложении. Не разные конфиги зависимостей, а разные классы DI-контейнеров.

Конечно, компромисс с хардкодом трейта есть, другой вопрос - насколько он ограничивает?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Ну так то маппер.
То есть инъектим. А на выходе он дает тебе массив, который ты инъектишь в сущность.

покажи развесистый aggregate root с кучей логики и с маппингом на пачку таблиц. Я вот не знаю, как ты это сделаешь без публичных атрибутов
А если объект сам себя наполняет - не надо танцев с VO, не надо публичных аттрибутов, сущность сама справляется. Покажу, я в процессе :)
 

MiksIr

miksir@home:~$
Да даже с одним контейнером. Ситуация, когда нужен один и тот же сервис с разной зависимостью - мне кажется вполне реальной. Ну не полезу сейчас искать случаи, может у меня и не случалось. Но какой-то экзотикой ситуация не выглядит. Например, в С нужно передать А с db1, а в D нужно передать A с db2. Как ты будешь разруливать это?
 

MiksIr

miksir@home:~$
Ага. Как, если конфиг ты втыкаешь в трейт статикой? Или ты уже там как-то код перепридумал?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@MiksIr, да, я всю статику кроме хранилища дефолтного контейнера убрал за ненадобностью
PHP:
/**
* This trait provides implicit injection of services from a container
*/

namespace GK;
use Pimple\Container;

/**
* Class DIContainerTrait
* @package GK
*
* @property \Monolog\Logger logger
* @property \Aura\SqlQuery\QueryFactory qbf
* @property \Aura\Sql\ExtendedPdo db
* @property \Slim\Collection $settings
* @property string $dollar_rate
*/
trait DIContainerTrait
{

    /**
     * @var Container
     */
    public static $container;

    /**
     * @var Container
     */
    protected $customContainer;
    /**
     * @return Container
     */
    public function getContainer() : Container
    {
        return $this->customContainer
            ?? static::$container
            ?? static::$container = (function(){$t=__TRAIT__;return  $t::$container;})->__invoke()
        ;
    }

    /**
     * @param Container|null $c
     */
    public function setContainer(Container $c)
    {
        $this->customContainer = $c;
    }
код, конечно, немного безумный и экспериментальный :)
 
Последнее редактирование:

MiksIr

miksir@home:~$
Не соображу, что бы объектам одного класса одну зависимость сделать разной - что делать то? Разные контейнеры подсовывать?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@MiksIr, тут видно что объекту можно назначить индивидуальный контейнер.
Аналогично можно задать индивидуальный конфиг зависимостей.
 
Последнее редактирование:

MiksIr

miksir@home:~$
тут видно что объекту можно назначить индивидуальный контейнер.
Так меня и смущает, что ради одной зависимости - делать еще один контейнер.
И потом, если будет много контейнеров... а кто будет инжектить контейнеры в классы? Еще один контейнер? ;)
 

fixxxer

К.О.
Партнер клуба
@grigori, изврат какой... ну вот и получается, что ты строишь наследование на LSB, то есть по сути две иерархии классов, напрашивается вынести отдельно без извращений :)

JS-разработчик, кстати, написал бы так:

PHP:
?? static::$container = (function($t){return $t::$container;})(__TRAIT__)
С переходом на AST подобные конструкции отлично работают и в php.
 

fixxxer

К.О.
Партнер клуба
Лучше так:

use Autowired\Db;
use Autowired\Logger;

:D

Кстати, а как в composer сунуть custom autoload function по psr-префиксу?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@grigori, изврат какой... ну вот и получается, что ты строишь наследование на LSB, то есть по сути две иерархии классов, напрашивается вынести отдельно без извращений :)

JS-разработчик, кстати, написал бы так:

PHP:
?? static::$container = (function($t){return $t::$container;})(__TRAIT__)
С переходом на AST подобные конструкции отлично работают и в php.
Да, максимально возможная рабочая форма извращения, специально - рвать шаблоны, исследовать пределы возможного. Работает намного быстрее привычного.
А что именно плохо?

LSB здесь просто так, чтобы можно было задать индивидуальный контейнер какому-то классу. Наследования нет, lsb только дает поддержку. Что именно плохо?

по лямбде - ага, так короче, спасибо :) огорчает только то, что PHPStorm не поддерживает полный синтаксис
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
А что именно плохо?
Ты получаешь кучу классов с неопределенным количеством зависимостей. В смысле, ты видишь класс, но конструктора нет, заюзать можно что угодно. Снижается читабельность, накладывается отпечаток на дизайн: ты меньше задумываешься о слоях, вот у тебя тут всё по рукой.

Когда ты используешь сервисы в entities/VO — это тоже проблема дизайна, усложняется как понимание кода, когда объект а-ля DateTime может лезть в базу, так и тестирование (ведь нужно что-то там мокать, в случае нормальных VO ты просто создаешь инстанс прямо в тестах).

Ты получаешь зависимость от этого трейта и SL. Ты получаешь проект «в себе», без возможности реиспользования. Ты можешь это парировать тем, что там, где нужна реюзабельность, ты не будешь использовать трейт, но ни ты, ни твои коллеги не знают где находится эта грань.

Для решения чисто технических проблем типа «два сервиса, один класс», тебе потребуется пилить костыли с переопеределением $container, менять код самого класса.

У тебя 100% будет куча конфликтов в большой команде, когда все побегут добавлять свои сервисы в @property в трейте. Со временем это превратиться в мусорку, не говоря про то, что это уже выглядит странно (dollar_rate? pdo? settings?):
PHP:
* @property \Monolog\Logger logger
* @property \Aura\SqlQuery\QueryFactory qbf
* @property \Aura\Sql\ExtendedPdo db
* @property \Slim\Collection $settings
* @property string $dollar_rate
Тут есть ещё одна проблема дизайна: \Slim\Collection $settings с первого взгляда — вполне нормальная зависимость, но дело в том, что часть сервисов действительно требует прямой зависимости от настроек проекта, а часть — через аргументы методов, т.к. сами значения могут прийти как из настроек, так и из другого источника (от админа, юзера, etc.). И здесь появляется соблазн везде использовать $settings, а потом перепиливать кучу классов, когда где-то потребуется другой источник данных. Аналогично со всякими getCurrentUser(), кстати. Появляется зависимость от конкретного контекста, т.к. сам подход провоцирует.

В конце концов, мы не роботы, а люди, есть чисто психологические моменты. Работать в проекте, который пронизан сомнительными практиками — это лишний стресс. Когда ты понимаешь, что ради сиюминутной выгоды, кто-то написал такой ущербный код, что для реализации простой с точки зрения менеджера задачи, тебе придётся изрядно потрудится, ты хочешь найти этого человека и объяснить ему кто он такой. Но такой человек обычно уже не работает в этом проекте. Он гуляет от проекта к проекту, щедро делясь своими архитектурными решениями, исследуя пределы возможного.
 
Последнее редактирование:
Сверху