Ответы пользователя по тегу Symfony
  • Использование транзакций в сервисах?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Если сервис 1 и сервис 2 должны юзаться еще где-то, имеет смысл вынести их функциональность либо полностью отдельно, без транзакций, либо частично в публичный метод. В 3-ем юзать одну транзакцию.

    Если так не делать - работа с событиями после flush, и ему подобными становится как минимум сложной.
    Ответ написан
    Комментировать
  • Как в symfony 4 протестировать сервис?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Зачем вам контейнер? Создаете себе сервис через new, мокаете его зависимости.
    Ответ написан
    Комментировать
  • Как написать ядро в ядре для перехвата ядра?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    То что вы хотите - путь боли. Смешав банку варенья и банку говна вы получите две банки говна. Есть вариант по проще:
    1. Разделите роуты нового и старого nginx-ом. Это значит, что у вас таки две точки входа.
    2. Примите за правило: в новом НЕ использовать старое, в старом допускается использовать новое.
    Ответ написан
    1 комментарий
  • Получить контейнер doctrine в symfony 4?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Используйте dependency injection
    Ответ написан
    Комментировать
  • Как деплоить Symfony 4 проекты?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Например так:
    1. Собираешь проект.
    2. Архивируешь его.
    3. Rsync-ом его заливаешь на прод.
    4. Разархивируешь в катало с имееем версии.
    5. Переключаешь ссылку root каталога (nginx) на каталог с разархивированной версией.
    Ответ написан
    2 комментария
  • Как в Symfony Validator обнулить значение поля если оно не валидно?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Валидатор - валидирует. Вы же хотите и валидировать и мутировать данные, что как бы вообще не правильно. Но если ну очень хочется - да, пишите свое, правда не обзывайте это валидатором
    Ответ написан
    Комментировать
  • Yii2 или Symfony3?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    yii не подходит для enterprise проектов. Конкретно из за синглтона и ActiveRecord

    смю Комментарии Попросили проверить код, на что смотреть нужно?
    Ответ написан
    7 комментариев
  • В чем может быть причина очень долгой загрузки symfony3 на вагранте?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Очень вероятно - кэши и логи в var/. На их синхронизацию уходит куча времени.
    Под вагрантом рекомендую отключить автоматическую синхронизацию проекта и делать это из IDE по факту изменения файла.
    Ответ написан
    Комментировать
  • В чем преимущества ValueObject и DTO?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Преимущество VO в том, что это значения, которые уже проверены на границы и тип. В вашем примере валидация происходит в геттере - это мягко говоря бессмысленно. Вот вам пример
    class UUID
    {
        /** @var string */
        private $value;
    
        /**
         * @param string $value
         */
        public function __construct(string $value)
        {
            if (!preg_match('/^[\da-f]{32}$/', $value)) {
                throw new \InvalidArgumentException(
                    sprintf('Argument "$value" must be correct UUID, actual value: "%s"', $value)
                );
            }
            
            $this->value = $value;
        }
    
        /**
         * @return string
         */
        public function getValue(): string
        {
            return $this->value;
        }
    }


    Дальше в коде вам достаточно делать type hinting на тип VO и все, значение будет корректным. От рефлексии, или runkit вы все равно защититься не сможете.

    -- --

    DTO - это штука, для удобного транспорта данных, между разными частями системы. Например у вас есть метод, который на вход принимает 20+ аргументов (например регистрация), вызывать такое кодло вероятно будет не удобно, но собрав dto вы можете его передать одним аргументом и рассчитывать на то, что данные переданы с правильными типами. Граничные же значения придется проверить, так как в задачу "транспорта" не входит контроль правильности данных между системами, что его используют.

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

    Использование KV в контексте DTO/VO - дико хреновая практика, в очень редких кейсах ее использование оправдывает себя. Дело в том, что массив - это набор произвольных данных. Что бы писать надежный код - вам придется на каждом этапе делать проверку правильности этого массива. Что это за проверки?
    * все нужные ключи существуют
    * все значения по этим ключам правильных типов
    * массив не содержит левых данных

    как реализовать на php типизированую коллекцию типа как в СИ

    class MyTypedCollection implements \Countable, \IteratorAggregate, \ArrayAccess
    ...
    Ответ написан
    2 комментария
  • Немного вопросов по DoctrineORM и Symfony?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Я не понимаю зачем, если в php есть __call().

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

    Ситуация, когда нужно сделать индивидуальную обработку поля - ясна, но зачем в большинстве случаев засорять класс однотипными методами - мне не понятно. В чем смысл?

    За тем, что бы не засорять его никому не нужной магией.

    Я понимаю, что он скорее всего запросит id у объекта, но заче грузить в память целый объект, если я, например, заранее знаю его id?

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

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

    Почему же? Хотите работать с адресами - работайте с адресами, при чем тут пользователи?

    Как сделать ORM-аннотацию, чтобы поле типа "datetime" заполнялось текущими датой-временем?

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

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Есть ли порог когда использование ОРМ становится неоправданым/оправданым? Где он?

    Чаще всего он становится неоправданным из-за слишком высоких накладных расходов.
    Пример:
    User MANY_TO_MANY Group
    User MANY_TO_MANY Category

    Нужно найти все категории, по заданному списку групп пользователей. В случае Doctrine ORM вам придется загрузить список груп, дальше циклом пройтись по всем пользователям (если загрузка ленивая - это куча запросов), дальше уже по всем пользователям пробежаться и вытянуть все категории, плюс убрать дубликаты. Тут лучше plain sql, или query builder, как вам больше нравится.

    В случае же когда, накладные расходы не высоки И ORM будет более удобной - стоит юзать ORM. Например: у вас уже есть загруженный пользователь, хотите достать все его группы. В случае Doctrine ORM это может выглядеть как: $user->getGroups().
    Ответ написан
  • Как в doctrine Entity создать поле, которого нету в таблице и заполнить его?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    конечно можно. не прописываете аннотаций на свойство и все.
    Ответ написан
    7 комментариев
  • Проект со сложной логикой на Symfony – как проектировать? Примеры?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Как хранить бизнес-логику чтобы модели не превратились в монстров из десятков тысяч строк?

    Тут не совсем модели. Entity - это просто объект данных, умеет хранить их в себе и бросать исключения, если не правильные данные вставляете, все. Repository - умеет работать со своим Entity И БД.

    БЛ находится в классах сервисах.

    Читал про Command Bus где, если правильно понял, на каждое действие в системе – свой класс?

    Вообще под каждое дествие - не обязательно, тут нужно руководствоваться здравым смыслом.

    Как их организуете (их тогда будут сотни)?

    Иерархически. Путь к классу должен быть "понимаем".

    Есть ли смысл выносить каждую доменную модель в модуль/микросервис, хранить всю связанную логику где-то там внутри, а с остальными общаться по внешнему API?

    Только в тому случае, если вы уже делали такие же проекты и в точности знаете максимально точно границы каждого домена. Иначе - не стоит. Делайте монолитную систему, а разделение на микросервисы - только по факту необходимости.

    За ответы в клиентскую часть – отдельный сервис-фронтенд?

    Если в "сервис" вы вкладываете понятие простого класса, умеющего форматировать ответы вашего проекта - мысль здравая.
    Если ответы будут асинхронными (от сервера к другому) - имеет смысл выностить в отдельный клиентский класс.

    Каков оверхед?

    Ничтожный.

    Используют ли такое на практике?

    Да

    Какие подводные камни?

    Следствием серьезной декомпозиции в любом случае будут лишние сущности, чем раньше от них будете избавляться - тем лучше.

    Как не превратить кидание/получение событий типа PostBeforeEdit/PostBeforeEditHandler в "callback hell"?

    Если есть возможность отказаться от событийной модели - часто лучше отказаться.
    Листнеры доктрины конечно штука мощная, но работает не всегда очевидно.

    Функционал "PostBeforeEdit/PostBeforeEditHandler" часто дешевле и проще вынести в сервис, но опять же руководствуйтесь здравым смыслом.

    ACL Где храните указанную логику?

    Если ACL будет не тривиальный - готовьте себя к тому, что он будет размазан по уровню контроллеров.

    Какие структуры для описанного выше – best practice?

    Если есть возможность привести к одноуровневому виду - сделайте. Если с точки зрения бизнеса может потребоваться иерархическая (не фиксированной вложенности) ACL - до последнего убеждайте, что это плохая идея, не повторяйте чужих ошибок.

    В моём понимании это выглядит как куча трёхмерных кубов доступа "crud – group – entity – field", как это сделать более плоским пока только одна идея – делать кучу таблиц many-to-many.

    Гибкая настройка вплоть до каждого поля 90% что не нужна. Если можно свести к понятию скопов прав - сделайте это.
    Структуру можно предлагать только зная ваш проект.

    Версионирование. Как вы версионируете подобные проекты?

    Semver.

    А если нужна "N-1" рабочая версия на продакшене?

    Значит на прод попадает ваша версия с тегом "N-1"))

    Есть ли смысл разделять версии в рамках единой кодовой базы проекта и как (неймспейсы, конфиг, модуль, что-то ещё)?

    Храните яйца в отдельных корзинках. Если модуль развивается полностью отдельно и может быть вынесен как зависимость проекта в vendor - делайте.

    И, самое главное – как всё это совместить?

    • РУКОВОДСТВУЙТЕСЬ ЗДРАВЫМ СМЫСЛОМ
    • Принимаете жесткие соглашения по правилам написания кода, например такие
    • Постарайтесь убедить бизнес в том, что без покрытия кода автотестами будет дороже, нестабильней и дольше. + Пишите тесты. Если объем тестов в 4 раза больше кода, который они тестируют - это норм. У меня бывали случаи, когда для критичного функционала тестов было в ~16 раз больше, чем кода.
    • Жесткие, обязательные кодревью.
    • Если задача крупная - декомпозируйте ее.
    • Технический долг - возвращайте обязательно И как можно скорее.
    • Перед тем как писать код для работы с внешним сервисом - имеет смысл написать его эмулятор.
    • Спешите только в случае серьезных проблем на проде)). Фичи "на вчера" отличаются от фич "на потом" только приоритетом выполнения, более ничем.
    Ответ написан
    6 комментариев
  • Почему Symfony 3 так долго грузится?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Для production окружения 300-400мс многовато. На самом деле даже для development окружения с полной перестройкой кешей этого многовато. Проверьте даты создания файлов в каталоге var/cache, если они меняются с каждым запросом - у вас не production окружение. Если снизу на странице есть debug консоль - вы тоже не в production окружении. Так же проверьте включен ли логгер доктрины, он тоже много кушает. Что касается кэшей доктрины - имеет смысл их хранить в memcached.

    Если ничего не поможет - посмотрите через xhprof, что у вас так долго выполняется.
    Ответ написан
    7 комментариев
  • Как получить доступ к репозиторию в сущности?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Как получить доступ к репозиторию в сущности?

    Вы не должны этого хотеть. Это противоречит самому понятию Repository-Entity.
    Репозиторий - работает только с БД и только с этити.
    Энтити - только хранит в себе данные и более ничего.

    Не пытайтесь превратить Repository в ActiveRecord, добром это не закончится.

    Если вам нужны зависимые энтити - тогда пропишите их в аннотациях.

    Если нужно вызывать именно метод репозитория: это делается через DI в сервисах, либо через $this->getDoctrine()->getManager()->getRepository('AppBundle:MyEntityName') в контроллерах.

    Конкретно в вашем примере: вы должны объявить сервис, в который будет насетапливаться EntityManager.
    Ответ написан
    Комментировать
  • Как получить имя роута в расширении Twig (тест)?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Если я правильно понял и вы хотите проверить соответствие роута конкретному экшну, то решение есть по проще.

    <?php
    
    namespace MyVendor\MyApplication\AppBundle\Tests;
    
    use KoKoKo\assert\Assert;
    use KoKoKo\assert\exceptions\InvalidNotEmptyException;
    use KoKoKo\assert\exceptions\InvalidNotObjectException;
    use KoKoKo\assert\exceptions\InvalidStringException;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\HttpKernel\HttpKernelInterface;
    
    trait ControllerTrait
    {
        /**
         * @param Request $request
         * @param string  $controllerClass
         * @param string  $actionName
         * @throws InvalidNotEmptyException
         * @throws InvalidNotObjectException
         * @throws InvalidStringException
         */
        public function checkRoute(Request $request, $controllerClass, $actionName)
        {
            Assert::assert($controllerClass, 'controllerClass')->string()->notEmpty();
            Assert::assert($actionName, 'actionName')->string()->notEmpty();
    
            /** @noinspection PhpUndefinedMethodInspection */
            static::$kernel->getContainer()->get('router_listener')->onKernelRequest(
                new GetResponseEvent($this->getKernel(), $request, HttpKernelInterface::MASTER_REQUEST)
            );
    
            $parameters = $request->attributes->all();
    
            /** @noinspection PhpUndefinedMethodInspection */
            $this->assertInternalType('array', $parameters);
            /** @noinspection PhpUndefinedMethodInspection */
            $this->assertArrayHasKey('_controller', $parameters);
            /** @noinspection PhpUndefinedMethodInspection */
            $this->assertSame($controllerClass . '::' . $actionName, $parameters['_controller']);
        }
    }


    Используется примерно так:
    <?php
    
    namespace MyVendor\MyApplication\AppBundle\Tests\Unit\Controller;
    
    use MyVendor\MyApplication\AppBundle\Controller\MyController;
    use MyVendor\MyApplication\AppBundle\Tests\ControllerTrait;
    use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
    use Symfony\Component\HttpFoundation\Request;
    
    class MyControllerTest extends KernelTestCase
    {
        use ControllerTrait;
    
        public function testMyRoute()
        {
            self::bootKernel();
    
            $request = new Request();
    
            $request->server->set('REQUEST_URI', '/MyRoute');
            $request->setMethod('POST');
    
            $this->checkRoute($request, MyController::class, 'myRouteOfMyController');
        }
    }


    Тут KoKoKo\assert\Assert - это либа для валидации входящих аргументов
    Ответ написан
    Комментировать
  • Symfony Doctrine, как показать список/настройки всех таблиц?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Полный список этих моделей (имя класса)

    см. \Doctrine\ORM\EntityManager::getMetadataFactory

    Соответствующая таблица (+ Entity - их может быть несколько)

    там же, где и 1

    Проверить, соответствует ли структура таблицы в базе модели данных, а если не соответствует - то в каких полях есть отличия, отличающиеся ключи и т.п.

    Курим все тот же Metadata

    ну и кнопка пофиксить с предварительным просмотром sql.

    У вас на клавиатуре как раз 105 клавиш под это дело)) Если программист допустил ошибку в схеме БД и не изменили entity под нее - то он же и должен исправить эту ошибку.
    Ответ написан
    Комментировать
  • Как вы используете docker для разработки в Windows?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    между докером и виртуалкой можно примаунтить каталог, серьезных потерь производительности быть не должно. На счет шаред каталогов - действительно стоит отключить.
    Ответ написан
    2 комментария
  • Когда сбрассывать кэш на symfony?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    для этого предназначен dev режим. Правда в случае переколбашивания вами app/conf кэш может и остаться, тогда придется руками. Бывают так же затыки, когда структуру энтити изменяете.
    Рекомендую написать себе Makefile, или просто bash скрипт, который будет делать очистку, вызывайте его по мере необходимости, symfony "даст знать" ошибками, когда это нужно.

    В случае production окружения - кэш сбрасывается вручную при заливке
    Ответ написан
    Комментировать
  • Как определить константы для Entity?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    Можете заюзать подход: ValueObject. Это класс с конструктором и одним геттером. Все что он умеет - содержать в себе 1 значение.

    В энтити вы пишите сеттер с тайп хинтингом на VO. При этом в поле энтити записываете значение из vo. В геттере вы создаете новый инстанс VO на основании значения.
    Ответ написан
    Комментировать