Ответы пользователя по тегу Symfony
  • Entity в bunlde как реализовать relation без зависимостей?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Нужна ли связь на уровни доктрины associations mapping?
    Что-то мне подсказывает, что на самом деле — нет; и достаточно связи на уровне id(uuid) между сущностями

    Пример на отдаленную тему и одновременно на эту — про потребность НЕ связывать сущности почем зря

    60657ae9b337d223246268.png
    Ответ написан
  • Как использовать Doctrine для сохранения связанных сущностей?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Persistence by Reachability: Cascade Persist
    There are additional semantics that apply to the Cascade Persist operation. During each flush() operation Doctrine detects if there are new entities in any collection and three possible cases can happen:

    • New entities in a collection marked as cascade: persist will be directly persisted by Doctrine.
    • New entities in a collection not marked as cascade: persist will produce an Exception and rollback the flush() operation.
    • Collections without new entities are skipped.

    https://www.doctrine-project.org/projects/doctrine...

    /**
     * @OneToMany(targetEntity="Article", mappedBy="topic", cascade={"persist", "remove"})
     */
     private $articles;
    Ответ написан
    Комментировать
  • Как решить проблему с маршрутом?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Роуты /search/{region}/{city} и /search — разные роуты

    Я бы остановился на варианте с query параметрами и резолвил бы их в параметры контроллера таким образом:

    https://stackoverflow.com/questions/33982299/symfo...

    Пример экшна
    /**
     * @Route("/search")
     * @ParamConverter("region", converter="querystring")
     * @ParamConverter("city", converter="querystring")
     */
    public function index(string $region,  string $city) 
    {
      return new Response(print_r($name, true));
    }


    class QueryStringConverter implements ParamConverterInterface
    {
        public function supports(ParamConverter $configuration) 
        {
            return 'querystring' == $configuration->getConverter();
        }
    
        public function apply(Request $request, ParamConverter $configuration) 
        {
            $param = $configuration->getName();
            if (!$request->query->has($param)) {
                return false;
            }
            $value = $request->query->get($param);
            $request->attributes->set($param, $value);
        }
    }
    Ответ написан
  • Как передать команде полученной из контейнера 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 (при всем моем уважении), тк это не бизнес-логика, а некоторая транспортно-приложенческая... Статические конструкторы могут быть, но не для дто, а для определенных данных, отображающих бизнес-возможность.

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

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

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

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

    В команду сам контейнер может автоматом подставить аргументы, если это другие сервисы и одновременно FQCN, в случае примитивов — значения или явно передайте или нужно забиндить
    Ответ написан
    Комментировать
  • Как найти контроллер формы в 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!
    Ответ написан
    7 комментариев
  • Symfony - Repository Как избежать двойного кода в этом случае?

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

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

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

    Maksclub
    @Maksclub
    maksfedorov.ru
    Можете сделать фейковый аутентификатор
    Symfony\Component\Security\Guard\AuthenticatorInterface
    и подсовывать юзера с нужными правами в тестовой среде, тогда легко можно проверять права доступа

    Как сделать свой фейковый аутентификатор — подсмотрите тут, единственное вам форма не нужна, тк вы пользователя всегда будете отдавать того, которого хотите (тесты же). Нужно будет сделать методы для управления тем, какого именно юзера вы хотите сымитировать.
    https://symfony.com/doc/current/security/form_logi...
    Ответ написан
    Комментировать
  • Как получить товар с последней ценой?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Ответ по теме:
    • или сделайте поле lastPrice и храните последнюю цену там (ну или через кастомный гидратор ее в поле продукта вставляйте)
    • или выбирайте необходимые данные голым SQL/DQL в DTO объекты,
    • или на крайний — отдельно выбирайте последние цены для ваших товаров, но тут по сути некоторое подобие второго варианта


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

    Совет по запросу:
    Лучше сделайте не вложенным запросом, а через джойн
    SELECT product.*, last_prices.*
    FROM product
    LEFT JOIN (
         SELECT product_id AS id, max(id) as price_id
         FROM price
         GROUP BY product_id
    ) last_prices  ON last_prices.id=product.id
    Ответ написан
    Комментировать
  • Как выставить переменную окружения при acceptance тесте в codeception?

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

    Итого: Сделать так, чтобы приложение при запуске тестов смотрело в ту же БД, что и тестовый фреймворк

    Для Symfony пара вариантов:
    - сделать тестовый домен, который бы поднимался с тестовым Kernel
    - поднимать приложение с переменными окружения не через имитацию окружения в .env, а через само собственно окружение (грубо говоря на http://172.17.0.1 в переменных окружения (не в файле их имитирующем .env) должны стоять верные параметры подключения
    - использовать модуль Symfony с явным указанием env: test, но подойдет для функциональных тестов, а не приемочных
    Ответ написан
    1 комментарий
  • Почему не добавляет таблицу в БД после миграции?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Ошибка в миграции Version20200628093518
    Она не выполняется из-за того, что в ее инструкции описано добавление поля, которое уже есть в БД...
    [error] Migration DoctrineMigrations\Version20200628093518 failed during...


    Не известно, добавляется ли ваша таблица в этой же миграции или в следующих, но эта ошибка останавливает дальнейшие инструкции (в том числе создание вашей таблицы)

    Вам нужно привести БД в консистентный вид с миграциями таким образом, что схема бд должна совпасть с выполненными инструкциями, которые есть в таблице с отмеченными миграциями
    Ответ написан
    8 комментариев
  • Как правильно спроектировать репозиторий для использования с необязательными параметрами?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Изящно решает паттерн Criteria/Specification.
    Doctrine Specification Pattern или ваш реюзабельны...

    Он может быть сложным, но вашу задачу решает элегантно. Благо он уже упакован в библиотечку, которую вы найдете по ссылке выше.
    Ответ написан
    1 комментарий
  • Symfony 4 как написать middleware для меню?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Twig Extension

    Создайте свой экстеншн, передав ему ваши сервисы (репозиторий например)
    В методе getFunctions() зарегайте свою твиг-функцию

    Зарегайте его с тегом twig.extension
    Вызывайте в ваших шаблонах
    Ответ написан
  • Как осуществить пере адресацию после авторизации на ранее запрашиваемый ресурс?

    Maksclub
    @Maksclub
    maksfedorov.ru
    По дефолту работает так, как вы и хотите:

    By default, the form will redirect to the URL the user requested (i.e. the URL which triggered the login form being shown). For example, if the user requested http://www.example.com/admin/post/18/edit, then after they have successfully logged in, they will be sent back to http://www.example.com/admin/post/18/edit.

    Redirecting after Success¶


    Видимо у вас поведение изменено через always_use_default_target_path:
    Changing the default Page¶

    ...или иным способом...
    Ответ написан
    5 комментариев
  • Doctrine как разрешить состояние гонки?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Помимо варианта с Lock: ON CONFLICT (expression) DO NOTHING и похожие решения в других СУБД могут быть иногда довольно подходящими в виду простоты реализации...

    Это выглядит буквально вот так:
    $sql = <<<SQL
    INSERT INTO table (id, value) 
    VALUES (555, \'uniqValue\') 
    ON CONFLICT (value) DO NOTHING
    SQL;
    
    $this->em->getConnection()->executeQuery($sql);


    Если это некие логи или словари с простой логикой хранения, то в самый раз...
    Ответ написан
    Комментировать
  • Easy admin. Как проверить путь?

    Maksclub
    @Maksclub
    maksfedorov.ru
    EasyAdmin контроллеры довольно простые, вы просто наследуете EasyAdminController и называете свой контроллер так: EntityName + Controller, то есть для User это будет UserController.

    И для ваших контроллеров как и везде в Symfony вы можете использовать свои роли, например так:
    /**
     * @Security("is_granted('ROLE_CAN_EDIT_USER')")
     */
    class UserController extends EasyAdminController
    Подробнее:
    Аннотация Security
    Symfony Uses Voters

    Также ознакомьтесь с главой EasyAdmin: Security and Permissions¶, особенно про item_permission
    Ответ написан
  • Symfony - phpstan, как устранить это замечание?

    Maksclub
    @Maksclub
    maksfedorov.ru
    try {
         // логика
    } catch(\Throwable $e) {
         // тут например логируем
    
         // тут мы 3-м параметром для нового исключения добавили прошлое исключение
         // чтобы не потерять стектрейс изначальной ошибки, мало ли откуда текущий код был вызван
         // о чем и говорит PhpStan
         throw new MyException('msg', 0, $e);
    }
    Ответ написан
    Комментировать