• Как использовать DDD?

    @EvgeniiR
    https://github.com/EvgeniiR
    Пусть статья и комментарии будут разными агрегатами. У комментариев будет свой домен.

    Можно в контексте комментариев сделать свой класс статьи( Article ). Он даже не будет сущностью:
    class Article {
      private UUID id;
      
      private CommentsRepository comments;
      
      ...
      
      public void function addComment(commentData: commentData) {
        this.coments.add(new Comment(this.id, commentData));
      }
    }


    В контексте комментариев не обязательно нужна статья - нужен только её идентификатор.
    Также в контексте комментариев вовсе не нужны данные пользователя - только его идентификатор. Идентификаторы достаточно стабильная информация чтобы не бояться их шарить.

    И да, в данном случае мы имеем дело просто с декомпозицией системы, к реальному DDD это мало отношения имеет, потому что domain-driven-design, он domain-driven за счёт того что конексты обсуждаются с бизнесом, а не придумываются разработчиком, в данном случае мы просто берём оттуда немножко терминологии потому что она уже стала довольно общей.
    Ответ написан
    2 комментария
  • Зачем (в наше время) везде писать phpdoc комменты?

    @EvgeniiR
    https://github.com/EvgeniiR
    Посторонним В.,
    Мой вопрос не про комментарии в клиентском коде, а именно про пхп-док блоки для классов и их членов, т.к. они почему-то часто считаются стандартом де-факто.

    Дублирование типов в php-доках не нужно и никаким стандартом не является(если только внутренним-корпоративным).

    P.S. И да, если бы программисты хоть иногда, перед тем как что-то заюзать задавали себе вопрос, зачем они это делают, и нет ли способов делать дела лучше, индустрия разработки была-бы совсем другой.
    Ответ написан
    1 комментарий
  • Как подкорректировать Router?

    @EvgeniiR
    https://github.com/EvgeniiR
    private $classConfig;

    Использовать типизованные параметры

    $this->classConfig = new Config;

    Использовать DI и интерфейсы, ниже подробнее.

    $arr = $this->classConfig->getConfigs('routes'); // Вывод роутеров (Ниже пример)
        if ($arr) {
          foreach ($arr as $key => $value) {
                	$route = '#^' . $key . '$#';
                	$this->routes[$route] = $value;
            	}
        }

    Плохо. Почитайте про GRASP паттерны, особенно Information expert и, на будущее - Coupling/Cohesion(совсем-совсем на будущее, хотя можете глянуть докладик - тык, думаю сойдёт).
    Без регулярных выражений можно обойтись.
    Информация должна быть там где данные, не нужно доставать данные из какого-то объекта чтобы принять решение на основе их - попросите объект с данными принять решение.

    Как вариант - вместо этого должен инжектиться (по интерфейсу) какой-нибудь ControllerInfoProvider, или RoutesProvider, с методом вроде getControllerInfo($requestData);, который вернёт экземпляр DTO вроде
    /**
     * @psalm-immutable
     */
    class ControllerInfo {
        public string $controllerClass;
    
        public string $controllerMethod;
    
        /**
         * @var string[]
         * @psalm-var class-string[]
         */
        public array $beforeControllerMiddlewares;
    
        /**
         * @param string[] $beforeControllerMiddlewares
         * @psalm-param class-string[] $beforeControllerMiddlewares
         */
        public function __construct(string $controllerClass, string $controllerMethod, array $beforeControllerMiddlewares = [])
        {
            $this->controllerClass = $controllerClass;
            $this->controllerMethod = $controllerMethod;
            $this->beforeControllerMiddlewares = $beforeControllerMiddlewares;
        }
    }

    Ну и ещё можно postControllerMiddlewares, или, с более понятным названием вроде ResposeAwareMiddlewares, потому что миддлвари это хорошо.
    Ещё там аннотации для статического анализатора psalm, потому что ловить ошибки на этапе разработки запустив ./vendor/bin/psalm лучше чем ловить их после запуска кода, особенно в продакшене.

    Впрочем, на самом деле это чутка оверхед для вас, но когда-нибудь разобраться с этим всем стоит.

    $url = trim($_SERVER['REQUEST_URI'], '/');
    foreach ($this->routes as $route => $params) {
    if (preg_match($route, $url, $matches)) {
    $this->params = $params;
    if (isset($this->params['folder'])) {
    $this->params['folder'] = '\\' . ucfirst($this->params['folder']);
    }
    return true;
    }
    }
    return false;

    Не нужно в Роутере глобальные переменные парсить. Пусть до роутера это дело обработает какой-нибудь RequestParser, и создаст DTO-объект запроса, можно по psr-7, можно без него, чтобы проще было. Это не критично.

    По поводу параметров в контроллере - стоит обратить внимание на argument resolvers, и не заниматься этим в роутере. Вещь отличная и удобная, свои аргумент резолверы тоже пишутся просто.

    echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
            return false;

    Не роутера это дело, в stdout писать. Пусть кинет исключение, а ответ пользователю сформируется уровнем выше.

    $path = 'app\controllers' . $this->params['folder']

    Роутер не должен определять структуру директорий. Используйте полное имя в конфиге роутов, т.е. SomeController::class.
    Сваливать все контроллеры в одну папочку занятие сомнительное.

    Почему (зачем) предпочитаю хранить роутеры в файлах (в базе данных), если можно автоматизировать

    Как удобнее так и храните. Конфиг, база, аннотации, php массив - без разницы

    Нужно ли корректировать код, указанный выше или и так сойдет?

    Чем больше сделаете из описанного выше - тем лучше.

    Что лучше: загружать контроллер из роутера или загружать класс контроллер (проверки...), который будет загружать необходимый контроллер?

    Получили ControllerInfo, обратились в ControllersRegistry->getControllerClass(), получили инстанс контроллера.
    Далее либо вызвали нужный метод, либо, если всё же сделали - попросили аргумент резолвер распарсить и инициализировать аргументы:
    $args = $this->argumentsResolver->resolve($controller, $method, $request);

    Вызвали миддлвари, что-то вроде этого: https://github.com/thephpleague/tactician/blob/mas... , вызвали контроллер:
    $response = $controller->{$action}(... $args)
    Ответ написан
    Комментировать
  • Считается ли это плохим кодом?

    @EvgeniiR
    https://github.com/EvgeniiR
    get()

    Чего он делает? И где типы и имена параметров, где возвращаемые типы?

    preparePath() (он просто указывает путь, где именно брать данные (это не бд)),

    Ничего не понятно даже с комментарием.

    validate()

    Кого validate?

    paginate()

    И чего оно делает?

    Правильно ли я понимаю, что это грубое нарушение SRP?
    ...
    Ну а в самом изначальном классе оставить moveToStorage + get методы.

    Оставьте гейтвей к базе который умеет из неё данные доставать и в нужном формате возвращать, остальное в отдельные классы.
    Ответ написан
    Комментировать
  • Можно ли в контроллерах использовать другие контроллеры?

    @EvgeniiR
    https://github.com/EvgeniiR
    Владимир Голубь,
    Есть функция проверки пароля и логина, она берет информацию из env. Может это вообще model ? Она применяется в двух контроллерах.

    Почитайте про паттерн middleware, и проверяйте логин и пароль в одном месте - до вызова контроллера.

    В JS:
    https://expressjs.com/en/guide/using-middleware.html и
    https://stackoverflow.com/questions/12921658/use-s...
    Ответ написан
    5 комментариев
  • Обязательно ли в ларавеле использовать Eloquent ORM?

    @EvgeniiR
    https://github.com/EvgeniiR
    Обязательно ли в ларавеле использовать Eloquent ORM?

    Нет, не обязательно.

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

    Делайте сырые запросы к базе. Не нужно натягивать сову на глобус выборки на ORM.

    ORM помогает когда вам нужны модели с логикой для какой-то операции. Если цель - просто получить данные от базы - выборки через SQL, и маппинг на DTO по прежнему остаются лучшим выбором.
    Ответ написан
    Комментировать
  • Можно ли изменять конфиг файл yml в symfony 4 с помощью кода?

    @EvgeniiR
    https://github.com/EvgeniiR
    $this->getContainer()

    Это прикольно и удобно

    Это не хорошо.

    Править сконфигурированный контейнер можно в compiler passes

    Больше его нигде в коде не нужно доставать в принципе. Никаких $this->getContainer().
    Конфигурируйте и инжектите сервисы через конфигурацию - https://symfony.com/doc/current/service_container.... .

    Сейчас вы просто используете контейнер не по назначению.
    Ответ написан
    Комментировать
  • Есть ли в PHP общий класс от которого наследуются все классы?

    @EvgeniiR
    https://github.com/EvgeniiR
    Хочу здесь принимать объекты конкретного типа, а в интерфейсе нельзя указать общий тип для всех классов

    Вы нарушаете Liscow Substitution Principle когда в наследниках требуете более конкретный тип чем в родителе, не нужно так делать.

    Для экземпляров любых классов есть тайп хинт object, но он вам не поможет по описанной выше причине.

    Смысла в общем интерфейсе для всех репозиториев нет.
    Ответ написан
    5 комментариев
  • Как вытягивать данные из БД в реальном времени?

    @EvgeniiR
    https://github.com/EvgeniiR
    сделать функцию которая проверяет на наличие новых записей из БД каждую секунды. Но я думаю что этот метот будет грузить сервер и пк. если ещё какой-то другой способ который выводить новые данные в момент его создания.

    Да, и он описан прямо в вашем тексте выше. Не ходите в БД в цикле, а реагируйте на приходящие через вебсокет сообщения.
    То есть, сначала записали в БД, потом вызвали остальную логику/кинули ивент уровня приложения - любым способом.

    spoiler
    Слушать изменения в БД тоже можно, конечно, например через listen/notify+процедуры в pg, но явно не нужно. Пусть логика уровня приложения остаётся на уровне приложения.
    Ответ написан
  • Как правильно реализовать MVC и MVP?

    @EvgeniiR
    https://github.com/EvgeniiR
    Вы правильно поняли, что в MVC контроллер не является связующим звеном между View и Model, то что в вебе называют MVC на самом деле является MVA - https://en.wikipedia.org/wiki/Model%E2%80%93view%E... .

    MVC это один их тех терминов про которые много кто говорит но который почти никто не понимает, увы. В приведённых статьях с Хабра просто производится подмена понятий

    Тут же на тостере, из старого, советую пару годных описаний - раз и два. Там в принципе всё что вам нужно знать на текущий момент
    Ответ написан
    1 комментарий
  • Symfony 4 autowiring и наследование?

    @EvgeniiR
    https://github.com/EvgeniiR
    1. Требовать в конструктор все необходимые для инициализации родителя зависимости. Наследуясь вы берёте на себя обязательства инициализировать экземпляр класс родителя, соблюдать контракт, судя по вопросу вам стоит обратить внимание на п.2:

    2. Избавиться от наследовния - лучше во всех планах, в подавляющем большинстве(если не в 100%) ситуаций.
    Ответ написан
    Комментировать
  • Doctrine ORM: можно ли не мапить объекты целиком?

    @EvgeniiR
    https://github.com/EvgeniiR
    можно ли не мапить объекты целиком?

    Можно забыть про костыли с маппингом некоторых полей объекта, и описывать отдельные DTO-классы с нужными полями.

    Документация - https://www.doctrine-project.org/projects/doctrine...

    p.s. Сущность при использовании DataMapper != табличка в БД.
    Ответ написан
    4 комментария
  • Как вытащить jsonschema со встроенными ссылками?

    @EvgeniiR
    https://github.com/EvgeniiR
    Json Shema - валидный JSON, поэтому:
    - Декодировать json(схему)
    - Пробежаться по полученному вложенному массиву, и каждый раз когда в ключе попадается "$ref", заменять это поле целиком на объект, путь которого в значении(ссылке).

    Придётся лишь чуть-чуть повозиться с тем чтобы корректно преобразовать адресс ссылки в абсолютный путь в массиве.

    Upd: Но возможно это не самая лучшая затея, т.к. $ref это не просто сокращение синтаксиса, но ещё и возможность рекурсии (дока), которую вы не сможете развернуть без ссылок.
    Ответ написан
    5 комментариев
  • Нормально ли изменять параметр функции внутри функции без объявления переменной?

    @EvgeniiR
    https://github.com/EvgeniiR
    Нормально ли изменять параметр функции внутри функции ...

    Не очень.

    В вашем примере:
    const myMyltilpy = (num) => {
        return (num < 0) ? 1 : num * 10;
    }


    https://developer.mozilla.org/en-US/docs/Web/JavaS...
    Ответ написан
    5 комментариев
  • Laravel отношения или обычные запросы с LEFT JOIN?

    @EvgeniiR
    https://github.com/EvgeniiR
    При выборах модификации данных не происходит, их можно спокойно делать через raw SQL.

    Более того, это хорошая практика, и сущности не являются хранилищами данных по определению в принципе.

    По правилам ORM мне все нужно делать ч/з отношения

    Нету такого правила.
    Просто на Ларавель часто проекты делаются тяп-ляп и в продакшн, и в сущности пихается все подряд.
    Ответ написан
    Комментировать
  • Можно ли в репозиторий использовать для сохранения обьектов?

    @EvgeniiR
    https://github.com/EvgeniiR
    Save your repository from save

    Можно ли понимать репозиторий как это по сути промежуточное звено между приложением и внешним хранилищем

    Это будет уже не репозиторий а гейтвей к хранилищу.
    Репозиторий - коллекция доменных объектов.

    Denis ,
    можно ли заменить CRUD репозиторием? Нет

    CRUD можно заменить готовыми решениями(postgrest etc.).
    Ответ написан
    2 комментария
  • Является ли залогом безбажности тщательная проверка всего кода, и что делать, если в нашей команде - является?

    @EvgeniiR
    https://github.com/EvgeniiR
    Эпиграф. Буквально час назад написал код из 70 строчек, в котором при первой перепроверке нашел 3 бага, при второй - еще 1, а при третьей - еще 1.

    А как считаются строки? Если это 70 строк одной процедуры с циклами то это проблема.

    Последний был алгоритмическим - я в цикле изменял то, по чему идет этот же цикл. И такое постоянно.

    Помимо того, что "тесты пишутся", важно как они пишутся. Я видел даже портянки на десятки строк которые называли "unit-тестами".
    Описанный баг тесты должны ловить. В хорошо покрытом тестами коде, они должны проверять все ветвления(условия) в методе, и возвращаемые значения, в т.ч. граничные условия.

    А ещё стат. анализ, иммутабельность.

    Так, как думаете, в релизе багов действительно не будет?

    Не делайте релиз крупным событием, релизьте часто(1 и более раз в день) и по чуть чуть, набирайте ответственных разработчиков.

    Может, вообще не писать авто-тесты? Ведь по трудоемкости проще посмотреть что-то в коде, чем написать тест на этот кейс.

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

    Или писать их только на сложное, то есть не делать их юнит-тестами, строго говоря.

    Вообще, посмотрите этот доклад - https://www.youtube.com/watch?v=VDfX44fZoMc .
    И да, чтобы удешевить покрытие кода unit-тестами, нужно уменьшать связность в коде, иначе утонете в моках.
    Улучшение проектирования - одна из основных задач unit-тестов(и основная задача при использовании tdd).
    Ответ написан
  • Как организовать структуру Symfony проекта для взаимодействия со сторонним API?

    @EvgeniiR
    https://github.com/EvgeniiR
    Вопрос написан сумбурно и не понятно. Какая решается задача - не ясно совсем.

    1. Можно ли каждую сущность представить сервисом?

    Нужно разобраться с терминологией "сущность представить сервисом" - каша какая-то. Сущность это сущность, сервис это сервис.

    Как получать данные для таких сущностей?

    "Получать данные для сущностей" это, опять же, каша какая-то. Пишите гейтвей к апишке и грузите данные.

    Service/PostService.php

    И нормально именовать тоже нужно учиться. PostService - ни о чём, что делает класс - не ясно.

    Создал отдельный сервис ApiService.php

    Снова название ни о чём.

    от которого наследуются сервисы которым нужно получать данные из api.

    Наследование - выпилить и забыть как страшный сон. Экземпляры API - клиента через DI поставлять в те классы, где он нужен.
    Ответ написан
    Комментировать
  • Почему советуют не выбирать yii2 для разработки?

    @EvgeniiR
    https://github.com/EvgeniiR
    1. Yii мёртв. Устарел лет на 10 по подходам и кодовой базе, и не развивается.
    2. Плохой дизайн. Глобальное состояние для всего, наследование от базового класса модели, валидация через массивы там же, наследование для расширения всего и вся и прочая чушь. Отсутствие многих удобных фич типа нормального DI/аргумент резолверов, чего только стоит гибкость конфигурации сервисов в Симфе.
    3. Свои велосипеды вместо чего-нибудь готового
    4. Все компоненты прибиты гвоздями и не заменяются своими. Это делает код на нём нерасширяемым и нетестируемым(Ну то есть в теории переписав пол фреймворка и 100500 своих адаптеров можно писать нормально, но те кто хочет писать нормально просто уходят с Yii).
    5. Слабое комьюнити которое сидит на нём потому что не осилило ничего другого / генерирует CRUD`ы через Gii(Заменить бы их уже не postgrest и прочие обёртки над базой) / инертные кодеры которым без разницы чего делать лишь бы на хлеб хватало.
    6. Все фреймворки далеки(очень) от идеала, но Yii сильно отстаёт от прочих.
    Ответ написан
    Комментировать
  • Какая архитектура должна быть у простого апп для показа данных, полученных из json?

    @EvgeniiR
    https://github.com/EvgeniiR
    По сути функционала там - Gateway к апишке и Presenter какой-нибудь. Это можно явно и выразить. Среди кучи кучи хуков(protected onSomethingHappened()) и статических вызовов Utils сложновато проследить поток выполнения. (Utils выпилить).

    Ну и по мелочи:
    - Выпилить кучу закомментированных строк
    - Привести нейминги к единому стилю, желательно без всяких префиксов
    - Не обрабатывать одни и те же условия по нескольку раз в рамках приватных методов одного класса.
    Ответ написан
    2 комментария