• Как передать команде полученной из контейнера DI аргумент при выполнении symfony?

    Maksclub
    @Maksclub
    maksfedorov.ru
    $greetInput = new ArrayInput($arguments);
    $greetInput->setInteractive(false);
    
    $returnCode = $command->run($greetInput, $this->output);
    Ответ написан
  • Как в Symfony загрузить DTO в Entity?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Вообще это плохая практика — маппить на сущность дто.

    Почему: сущность — некоторый бизнес-обьект, он контролирует переходы состояния и инкапсулирует саму логику. Но сущность находится в контексте... бизнес-процесса, который выражен некоторым др видом классов (интеракторы или use cases, если терминами Боба Мартина), например в CQRS таким родом классов являются хэндлеры команд.
    Итог: с сущностями напрямую скорее не стоит работать, тем более в сущностях не должно быть сеттеров :) и методов превращения данных из дто в явном виде ака fromDto(), как указал Flying (при всем моем уважении), тк это не бизнес-логика, а некоторая транспортно-приложенческая... Статические конструкторы могут быть, но не для дто, а для определенных данных, отображающих бизнес-возможность.

    Кроме того, ваше решение чревато высокой связанностью — ваши сущности конструируются под дто, дто под сущности...

    Как делать хорошо:
    Сущности создавать только в рамках бизнес-процесса, то есть всегда явно и напрямую через конструктор или фабричный метод, воплощающий в себе бизнес-логику.
    Ответ написан
  • Slim 4 и Ошибка?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Could not detect any PSR-17 ResponseFactory implementations. Please install a supported implementation in order to use `AppFactory::create()`. See https://github.com/slimphp/Slim/blob/4.x/README.md for a list of supported implementations.

    Вам же написали что делать: нужно поставить одну из этих либ https://github.com/slimphp/Slim/blob/4.x/README.md...

    Перейдите по ссылке любой из списка и через композер поставьте, например
    composer require slim/psr7
    Ответ написан
  • Как писать бандл Symfony без боли и геморроя?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Можно писать бандл в контексте простого приложения:
    • создаете приложение простенькое
    • заводите внутри проекта через git submodule свой бандл
    • через composer-секцию autoload неймспейс бандла подключаете в автозагрузку
    • работаете с бандлом, при том он у вас имеет все плюхи
    Ответ написан
  • Можете привести пример принципа открытости/закрытости?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Декоратор — декорирующее поведение вынесено в др класс с др названием в рамках одной абстракции (соблюдается интерфейс). Это соблюдается open closed principle.

    Но если Декорирующее поведение помещают в сам исходный класс с if/else — это нарушение принципа.

    Пример: у вас есть бабушка, вы научили систему пенсий работать с этой бабушкой. Но в вашем приложении бабушка нужна одетой в защиту, в маске COVID-19 и с навыком спускаться на лыжах. Если все это поведение будет запихнуто в бабушку — вы нарушите принцип, тк придется бабушку править, а можно сделать декораторы "бабушка с лыжами" и "бабушка с маской" и все это будет все та самая корректная бабушка, которой легко сделать новое изменение новым декоратором, и не надо шатать все старое поведение.

    Декоратор — не единственный верный способ соблюсти, но хорошо иллюстрирует, например отличным способом может быть вычленение какого-то поведения в отдельную абстракцию
    Ответ написан
  • Чем отличается такой код?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Помимо удобства, if else без раннего возврата управления может усложнить программу, тк может понадобиться следить за состоянием результата

    https://refactoring.guru/ru/replace-nested-conditi...
    Ответ написан
  • Что значит запись: Collection collection = new ArrayList();?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Изучите ковариантность и контрвариативность типов.
    А также Liskov Substitution Principle из SOLID — объяснит мотивацию такого написания.
    Должно стать понятнее.

    Пример:
    Когда вы говорите, что во дворе стоит авто (Тип переменной), полученная (оператор присваивания) покупкой Nissan Quashkai, то выражение верно логически по соблюдению типов.
    Наоборот — нет, тк при покупке машины во дворе не появится Quashkai гарантированно :)


    Иными словами ArrayList является также Collection и потому все переменные этого типа являются корректными коллекциями,
    Также как все Ниссан кашкаи являются корректными автомобилями
    Ответ написан
  • Как исправить ошибку при создании команды?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Это значит, что через контейнер вы не передали в команду аргумент

    В команду сам контейнер может автоматом подставить аргументы, если это другие сервисы и одновременно FQCN, в случае примитивов — значения или явно передайте или нужно забиндить
    Ответ написан
  • Создание структуры по методу cqrs?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Вполне понятная и логичная структура. Скажем так — простая, логичная и популярная.
    Ответ написан
  • В чем отличие схемы, модели и сущности?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Модель — некоторая абстракция того, что мы проектируем (моделируем):
    - или модель бизнес-абстракции, будь то сущности (платёж, товар, заказ) с состоянием или будь то бизнес-процессы (заказ товара, списание баланса за покупку, увольнение) без состояния или из совокупность
    - или модель данных/хранения (например изображений)
    - или модель чего угодно, что мы спроектировали, кроме инфраструктурных деталей и связующих с ними деталей (контроллеров к примеру), если это не базовая суть нашего кода

    Сущность — бизнес-модель, которая имеет явный идентификатор и своё состояние (конкретный документ, конкретный юзер, конкретный платёж, где конкретность обусловлена некоторым уникальным идентификатором)

    Схема — абстрактный план чего-то (связей или процессов к примеру)
    Ответ написан
  • Логика в геттере возможна ли?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Нет

    Геттеры могут быть в транспортных классах и иногда в инфраструктурных/библиотечных.

    Иначе это нарушение инкапсуляции: открывают доступ к тому, что закрыли. Как и сеттеры — дают доступ к тому, что закрыто, но на запись.
    Крч чит, хотя соответствуют определению инкапсуляции и дату-хиддингу

    Геттеры повышают связанность системы из-за открытой доступности данных модуля/класса, именно возможностью заюзать знания о состоянии, что в средних и больших, а иногда и в простых, на ровном месте усложняет код
    Ответ написан
  • Как замокать приватное свойство в классе phpunit?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Имитация вашего объекта:
    class Relay
    {
        private $privateProperty;
    
        public function __construct()
        {
            $this->privateProperty = new \stdClass();
        }
    
        // метод, который работает с приватным свойством
        public function call()
        {
            return $this->privateProperty;
        }
    }


    Способ 1: Нативный PhpUnit + Reflection API
    use PHPUnit\Framework\TestCase;
    
    class RelayTest extends TestCase
    {
        public function testCall(): void
        {
            $reflectionClass = new \ReflectionClass(Relay::class);
            $reflectionProperty = $reflectionClass->getProperty('privateProperty');
            $reflectionProperty->setAccessible(true);
    
            // создаем наш объект БЕЗ конструктора
            $relay = $reflectionClass->newInstanceWithoutConstructor();
    
            // Меняем свойство и вызываем метод, работающий с этим приватным полем
            $reflectionProperty->setValue($relay, 1111);
            self::assertEquals(1111, $relay->call());
    
            // Меняем свойство и вызываем метод, работающий с этим приватным полем
            $reflectionProperty->setValue($relay, 'aaaa');
            self::assertEquals('aaaa', $relay->call());
        }
    }


    Способ 2: Через Codeception Stub
    class RelayTest extends TestCase
        public function testCall(): void
        {
            /** @var Example $stub */
            $stub = Stub::make(Relay::class, [
                'privateProperty' => 1111,
            ]);
            self::assertEquals(1111, $stub->call());
    
            $stub = Stub::make(Relay::class, [
                'privateProperty' => 'aaaa',
            ]);
            self::assertEquals('aaaa', $stub->call());
        }
    }

    Отступления и полезные советы:
    • Почему-то стандартный, давно используемый мною, способ с инъекцией в мок приватного филда не зашел и выдавал null всегда.
    • Юзайте Inversion of Control Principle, например инъекцию зависимости через конструктор или инъекцию через метод с присвоением в конструкторе NullObject. Оба способа будут хороши для тестирования.
    Ответ написан
  • Как найти контроллер формы в symfony?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Данный контроллер, указанный в шаблоне отрисовывает (создает) форму по пути bundle:controller:action, а вот обрабатывает другой/другие. Вообще текущий шаблон вызывается искомым контроллером или несколькими.

    Ищем контроллер по обработке формы:

    1a. Или открываете гугл-консоль вкладку Network -> XHR и при отправке формы смотрите — на какой роут идет отправка
    1b. Или открываете дебаг-консоль Symfony и при отправке формы смотрите — на какой роут идет отправка

    2. Ищите роут: bin/consol debug:route <route>, где <route> — путь от path, можно через grep искать: bin/console d:r | grep <route_part>

    Команда выдаст вам примерно такую инфу:
    $ bin/console d:r home
    
    +--------------+----------------------------------------------------------------+
    | Property     | Value                                                          |
    +--------------+----------------------------------------------------------------+
    | Route Name   | home                                                           |
    | Path         | /{_locale}                                                     |
    | Path Regex   | {^/(?P<_locale>en|ru)?$}sDu                                    |
    | Requirements | _locale: en|ru                                                 |
    | Class        | Symfony\Component\Routing\Route                                |
    | Defaults     | _controller: \App\Controller\HomeController::index() |
    |              | _locale: en                                                    |
    | Options      | compiler_class: Symfony\Component\Routing\RouteCompiler        |
    |              | utf8: true                                                     |
    +--------------+----------------------------------------------------------------+


    но там могут быть переменные (id например или слаг), нужно без них... в любом случае путь найдете по команде bin/consol debug:route название вашего роута (справа пути с выражениями, слева названия роута — его и подставить в первую команду нужно):
    users.show           ANY      ANY      ANY    /admin/users/{id}                                 
    app_login            ANY      ANY      ANY    /login                                            
    app_logout           GET      ANY      ANY    /logout                                                               
    oauth.fake           ANY      ANY      ANY    /fake_login                                           
    auth.signup          ANY      ANY      ANY    /signup


    3. Там где-то есть обработка формы, находите что-то типа if ($form->isSubmitted() && $form->isValid()) и уже внедряете свою капчу
    4. PROFIT!
    Ответ написан
  • Полиморфизм в базе данных, как организовать таблицы?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Разделяйте ответственности
    OrganisationSeller и PersonSeller могут наследоваться от Seller
    и для реализации такого можно реализовать такой паттерн Single Table Inheritance

    А вот обычные Organisation и Person связывайте с абстрактным Seller через обычную связь. Конкретные наследники будут лежать в зависимости от типа

    Итого будет так:
    (OrganisationSeller extends Seller) будет объектом Organisation.seller

    Готовые решения:
    Ответ написан
  • Symfony - Repository Как избежать двойного кода в этом случае?

    Maksclub
    @Maksclub
    maksfedorov.ru
    flush() нужно вынести, потому что вы можете заперсистить 100 объектов и сделать один flush — одна череда обращений к Бд. В вашем же случае будет 100 и более обращений к БД

    Чтобы сделать универсальный метод для персиста в репозиториях, раз уж вы решили так делать (многие считают это верным) — можно сделать универсальный код (трейт к примеру), в котором будет проверка через classMetadata, что сохраняемый объект нужного типа для репозитория (тут не забываем про наследование сущностей, is_a()и вот это все) и если придет не тот объект — кидать InvalidArgumentException

    НО! Если вы репозитории размещаете в своем домене/пакете/неймпсейсе, то вам не избежать дублирования кода, и это не так уж и страшно... Связанность и паутина наследований страшнее. Репозиториев все равно мало во всем коде относительно всей базы кодовой.
    Ответ написан
  • Что такое атрибуты в языке программирования?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Это аналоги аннотаций (которые есть уже в PHP через библиотеки типа Doctrine Annotations)
    5f2e640b82f02855914596.png

    и которые в свою очередь аналоги Java Annotations
    5f2e64693dc84892539290.png

    Тк в PHP это реализовывалось отдельным парсингом определенно оформленных комментариев — стало по сути отдельным языком и получило популярность в Symfony и др инструментах — решили внедрить как в Java нативно на уровне языка.

    Аннотации/атрибуты нужны для того, чтобы можно было писать логику в аспектно-ориентированном стиле, подробнее.
    То есть некоторая логика, которая выполняется несколько отдельно (декорирует и/или меняет поведение или ещё как-то иначе улучшает программу).
    Это очень выразительный, удобный способ для использования, хотя иногда и сложный для того, чтобы разобраться в кишках работы того или иного инструмента. Более подробно: Wiki: Аннотация (Java)
    Ответ написан
  • Как собрать массив определённого ключа другого массива?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    <?php
    
    // Ваши данные в упрощенном виде (без мусора)
    $array = [
        'numbers' => [
            [
                'numbers' => [
                    [
                        'formattedValue' => 111,
                    ],
                    [
                        'formattedValue' => 3333,
                    ],
                    [
                        'formattedValue' => 5555,
                    ],
                ],
            ],
            [
                'numbers' => [
                    [
                        'formattedValue' => 200111,
                    ],
                    [
                        'formattedValue' =>  2003333,
                    ],
                    [
                        'formattedValue' =>  2005555,
                    ],
                ],
            ],
        ],
    ];
    
    // Найдет все значения во всех вложенных элементах с нужным ключом
    // и сделает это рекурсивно — то есть во всех массивах и их детях.
    function searchValues(array $arr, string $search, array $res = []): array {
        array_walk_recursive($arr, function($value, $key) use (&$res, $search) {
            if ($key === $search) {
                $res[] = $value;
            }
        });
    
        return $res;
    }
    
    // Ищем в нашем массиве все значения с ключом `formattedValue` 
    // и складываем найденные значения в пустой массив (по дефолту), 
    // но можно передать 3-м параметром и имеющийся массив, тогда значения туда добавятся.
    $result = searchValues($array, 'formattedValue')
    
    var_dump($result);

    sandbox.onlinephpfunctions.com/code/d91d34104073f8...
    Ответ написан
  • Как подключить Doctrine в свой проект?

    Maksclub
    @Maksclub
    maksfedorov.ru
    По мануалу создание EM:
    Официальная дока простая же: https://www.doctrine-project.org/projects/doctrine...

    Создаётся подключение, создаётся массив с путями, где лежат сущности и на основе этой инфы создаётся EntityManager

    На примере Slim создание и регистрация EM:
    Можете подсмотреть как тут настраиваюсь доктрину.
    Да, в контексте контейнера, но вы увидите как это все не сильно отличается от оригинального мануала:
    www.slimframework.com/docs/v3/cookbook/database-do...

    Репозитории:
    Репозитории в доктрине либо общие (с ограниченным интерфейсом EntityRepository), либо создаются фабрикой вами или вообще руками как по последней ссылке... Если работали с симфони, то вы знаете, что надо было зарегать репы...
    Ответ написан
  • Как правильно в доктрине построить запрос для фильтра со сложными условиями?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Вы в цикле для разных фильтров добавляет в один объект QueryBuilder параметры с одним ключом values, потом при сборке запроса Doctrine достает первый найденный Parameter с ключом values (а их таких несколько, но ей достаточно первого).

    Код Доктрины:
    5f21da43a9cea902909869.png

    То есть для цвета, года, уровня — всегда достается первый параметр с ключом values

    Предлагаю подставлять вместо этого слова разный алиас, например key:
    [$normalizedKey, $key] = $this->getKeys($qb);
    
    $qb
    	->andWhere(sprintf('%s IN (:%s)', $normalizedKey, $key))
    	->setParameter($key, $this->getValue());
    Ответ написан