Service Locator Repository injection with trait

MiksIr

miksir@home:~$
И, на самом деле Sufir верно про Yii1 вспомнил. Когда в какой-то момент ты понимаешь, что этот Yii:app() прошил все приложение такими толстыми нитками, что распутать это, выдрать что-то, понять кто от чего зависит начинает доставлять проблемы. Хотя, конечно, писать так быстрее и кажется, что легче. Не знаю, удивляюсь, что ты не словил таких ощущений с Yii1.
 

Вурдалак

Продвинутый новичок
И, на самом деле Sufir верно про Yii1 вспомнил. Когда в какой-то момент ты понимаешь, что этот Yii:app() прошил все приложение такими толстыми нитками, что распутать это, выдрать что-то, понять кто от чего зависит начинает доставлять проблемы. Хотя, конечно, писать так быстрее и кажется, что легче. Не знаю, удивляюсь, что ты не словил таких ощущений с Yii1.
Приятно наблюдать за эволюцией мнений на форуме. Я ещё помню срачи на тему DIC vs SL.

Мне не очень понятна экономия на количестве написанных строк, когда вместо 3-х чистых строчек пишутся 2 более грязных. Мы читаем код гораздо чаще, чем изменяем его. Сэкономив минуты на изменении, мы теряем часы при чтении.
 

AmdY

Пью пиво
Команда форума
Приятно наблюдать за эволюцией мнений на форуме. Я ещё помню срачи на тему DIC vs SL.
Мнение очень сильно зависит от инструментария, одно дело каждый раз DI прописывать в yml или других конфигах, другое дело вообще не париться как в случае laravel.
Или как очень сырая 2-я версия доктрины с её багами, крохотным функционалом, проблемами с производительностью, это надолго отталкивает от инструмента и идеи.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@MiksIr, в трейте __CLASS__ резолвится в имя того класса, в котором трейт использован.
PHP:
Trait T{
    function foo()
    {
        echo __CLASS__.PHP_EOL;
    }
}

class B {
    use T;
}

Class C extends B{
}

$x = new C;
$x->foo();
Чуть позже пропишу в трейте массив зависимостей, и трейт будет работать по нему для того класса, в котором он используется. Будет почти полноценный DI с конфигом зависимостей :)
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
PHP:
$di['\My\Model']->foo->Bar()
Суть вопроса - откуда взять этот $di?

Пример: объекты, которые пришли из PDO:
PHP:
        $select = 'SELECT * FROM `sizes`';
        $sizes = $di->db->perform($select)
            ->fetchAll(
                \PDO::FETCH_CLASS | \PDO::FETCH_GROUP,
                Size::class
            );
Получен массив $sizes из объектов типа Size.

Как в них передать нужные сервисы? Конструктор недоступен. Объекты уже созданы через PDO.
Циклом сеттерами в каждый объект список сервисов? Можно, но относительно медленно.
Циклом целиком контейнер в сеттер? Можно, но грязно.

Прописать один трейт в классе Size удобнее и быстрее :)
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@WMix, там еще пример кода на 2 сроки ниже, он тоже относится к вопросу.

Вопрос еще раз: откуда взять $di в объектах, которые получены из PdoStatement::fetchAll() или из любых других сторонних библиотек?
Ты хочешь в тысяче объектов, которые получены в результате запроса, вызывать include c магической константой и циклом? Это худшее решение из всех возможных.

Вы предпочитаете не думать об этой проблеме, пусть разбираются авторы фреймворков? Это раздел "Теория", и я пытаюсь придумать, а можно ли сделать лучше?

Вот один недостаток вижу: хардкод имени трейта.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Ты сам себе выдумал проблему и пытаешься её героически решить, как тут у нас любят говорить. В сущности $di нафиг не нужен.
 
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
В Size не нужны сервисы. А если в каком-то случае вдруг нужны, то может помочь http://thinkbeforecoding.com/post/2009/03/04/How-not-to-inject-services-in-entities.
Это уже обсуждали. В статье кроме "ЛАЛАЛА Инъектить в сущности $%^-ло!" других обоснований нет. Не могу принять, извини.

Ты сам себе выдумал проблему и пытаешься её героически решить, как тут у нас любят говорить. В сущности $di нафиг не нужен.
Очень полезный комментарий, так и есть, я занимаюсь теоретическими исследованиями странного.
Я пытаюсь разработать теоретическую модель, которая делает то же, что и обычный DI, но быстрее. Ценой компромисса по хардкоду имени трейта.
Да, DI нужен не то чтобы всем, мало кому он нужен, если есть анемичные модели. Простите, ValueObject. Спасибо за внимание, проходим дальше в салон :)
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Ты никогда не задумывался на тему того, почему ни одна из ORM не предоставляет возможности для инъекции сервисов через конструктор? Во всяком случае, это нигде не преподносится как best practice. Сущность представляет из себя состояние + статическое поведение, которое контролирует и управляет переходом из одного состояния в другое. Ты все проблемы пытаешься свети в чисто техническую плоскость: «конструктор закрыт! нужно копать в обход!». Ты не задаёшься вопросом «нужно ли копать?», ты просто решаешь, что нужно копать в обход.

http://phpfaq.ru/humor/anecdotes#jump
 

WMix

герр M:)ller
Партнер клуба
@WMix, там еще пример кода на 2 сроки ниже, он тоже относится к вопросу.
если правильно понял
PHP:
class Mapper{

    privat $entity;
    private $mapping;
  
    public function __construct( $entity, $mapping ){
        $this->entity = $entity;
        $this->mapping = $mapping;
    }
  
    public function map( array $array ){
        $obj = clone $this->obj;
        foreach($this->mapping as $key => $val){
            if(isset($arr[$val]))
                $obj->$key = $arr[$val];
        }
       return $obj;
    }
}

class Entity{
   public $name;
}

class Model{
    private $db;
    private $mapper;
  
    /**
     * @return Entity
     */
    public function get( $id ){
        return $this->mapper->map(
            $this->db->fetchAll('
                select name
                from table
                where id = ?',
                [$id]
            )
        );
    }
}

return [
  'Model' => function( $di ){
    return new Model( $di['db'], new Mapper( new Entity, ['name' => 'name']) );
  }
];

$di['Model']->get(42);
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Конструктор не закрыт, конструктор не вызывается. Создавать объекты и прописывать значения в приватные поля класса без вызова конструктора - это не best practice, это дефолтное поведение PDO.

Сущность представляет из себя состояние + статическое поведение, которое контролирует и управляет переходом из одного состояния в другое
У меня есть пачка сущностей, и есть их поведение. Мне не нужен конструктор. Я хочу обеспечить поведение сущности: расчет ее значения на основе внешней константы, курса. Можно сделать расчет в отдельной модели, можно операцию сложения делать отдельной моделью, но я не хочу писать SimplePHPEasyPlus. Я готов на компромиссы.

То, что ты выделяешь запросы в базу в отдельный объект от данных, которые получаются в результате этого запроса, я знаю. Я хочу писать запросы в классе сущности. Хорошо бы объяснить почему ты хочешь именно так, а не иначе.
 
Последнее редактирование:

Adelf

Administrator
Команда форума
@grigori, ты хочешь писать запросы в классе сущности. Ты хочешь там же рассчитывать её значение на основе внешнего курса. Осталось только научить её варить кофе? :)
Если тебе нужен сервис в сущности, то это смешение Анемик и Рич стилей, причем совсем неприятное. Не проще тогда все делать анемиком? Сервис ValueCalculator и в него инжектить то, что тебе надо?
 

WMix

герр M:)ller
Партнер клуба
Я хочу писать запросы в классе сущности
если вместо entity вернуть обертку типа proxy которая содержит и entity и обьект зарпосов, умеющая (proxy обертка) понимать к чему ты обращаешься то будешь иметь
"запросы в классе сущности"
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@Adelf, это обсуждалось совсем недавно - простые расчеты делаем на месте. Sql выносится в сервис, который создает анемичную модель с данными, смысла в которой я пока что не вижу.

@WMix, зачем именно плодить эти дополнительные сущности, если можно без них?

Господа, я знаю как вы пишете и понимаю, что вам не нравится. Объясните почему. Все понимаете, сказать не можете - значит, не понимаете, а просто верите. Это не опрос и не референдум, господа, это исследование, и отлично было бы прочитать логику. Мнения я знаю отлично :)
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
@grigori, такой подход (с "ручным" активрекордом, SL в базовом классе/трейте, и методами типа loadById) я использовал лет 5 назад, пока модели очень простые, это работает, как только начинаются хитрости, все дерьмо размывается по самой модели и внешним сервисам, и получается адъ.

Если это read only модель, ничего прям плохого, наверное, не произойдет, но тестировать это неудобно.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Если это read only модель, ничего прям плохого, наверное, не произойдет, но тестировать это неудобно.
Скорее, CRUD-only.
Вы делаете VO для результатов SELECT. Логично, что ничего не меняется от объединения массива данных с SQL, который его порождает, или их разделения.
И наличие UPDATE тоже ничего не меняет.

Насчет тестирования есть мысль - все можно сделать очень даже удобно. И конфигурация зависимостей в стиле php-di отлично делается. Скоро покажу.

Логика обработки - да, надо отдельно. Однако, в web почти вся логика - в join-ах. Поэтому, можно представить сложный SELECT как таблицу, и его тоже представлять как immutable collection. Только не VO, а полноценные классы.

пока модели очень простые, это работает, как только начинаются хитрости, все дерьмо размывается по самой модели и внешним сервисам, и получается адъ.
понимаешь что наговносячил и анализируешь как отрефакторить
Но это же совершенно другая проблема, которая никак не связана с темой. Хитрость надо выносить. Если действие выходит из CRUD - нужно уходить наверх.
Класс должен быть не больше 300 строк. Метод должен помещаться на экран.
Это как автодор недавно обосновывал, что плохие дороги лучше, потому что народ ездит медленнее, и меньше разбивается :)
Я же не для индусов это пишу тут.
 
Последнее редактирование:
Сверху