• Что идёт не так в пайпе Битбакета при сборке Laravel-приложения?

    Maksclub
    @Maksclub
    maksfedorov.ru
    composer install --ignore-platform-reqs
    Поставит, игнорируя требование расширения
    Ответ написан
    Комментировать
  • Как отрефакторить классы с неоднородными конструкторами?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Обычно делают REgistry

    FooInterface

    Service1Foo
    Service2Foo
    Service3Foo

    FooRegistry через теги или через call регаются конкретные сервисы, в фабрике получаем через type (если через call регали с нужным ключом) или в цикле прогоняя каждую зависимость и вызывая supports($type) у каждого из сервиса
    Ответ написан
    Комментировать
  • Зачем в обычном проекте может понадобиться использование __invoke?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    __invoke() метод позволяет превратить объект в функцию, которую мы можем передавать/получать/резовлить через псевдо-тип callable.
    Этакий элемент функционального программирования.

    Хорошо подходит там, где мы выстраиваем pipe или reduce, для описания которых ФП подход является наиболее выразительным и лаконичным. Не знаком с архитектурой Ларавел, но могу предположить, что для авторизации прорисовывается некоторый пайплайн, где можно выполнять разные пайпы или фильтры для првоерки/валидации или чего-либо именно с таким подходом и это более лаконично...

    Пример:
    public function add(callable $pipe) {
        //..
    }
    
    ...
    
    $pipeline = (new Pipeline)
         ->add(fn() => true)
         ->add(fn(bool $enabled) =>'Maks enabled')
         ->add(new MaksHandler()) // тут класс с методом __invoke и работает как анонимка
    ;
    Ответ написан
    33 комментария
  • Как можно ускорить работу Exception?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    на расходы конкретно на исключения для 15 000 вызовов (на каждую строку по 1 исключению) будут около 0.01 секунды (сотая секунды крч) для моего локального компа (много, но и данных много)
    Пруф

    618e769ee759d433078044.png


    $error = $this->handle($json);
    if ($error === '') {
    // логируем что все ок
    } else {
    // логируем ошибку
    }

    Тут заменено не только это, а заменено что-то внутри в handle(), тк не летит исключение

    возможно в описании задачи вы не указали, что валидируете возможно через что-то симфовое, не отключив finger cross, который собирает весь стек-трейс или еще что-то... кажется, не хватает контекста для понимания проблемы

    в 15 тыс новых объектов проблемы может не быть. Такие задержки по времени выглядят как проблема на уровне логирования исключений, тк I/O операции над файлом долгие, а сбор трейса исключений и его запись — долгие потенциально.
    Уйдя от исключений вы возможно ушли и от логирования исключений.
    Ответ написан
  • Как исправить Ошибку авторизации?

    Maksclub
    @Maksclub
    maksfedorov.ru
    UserInterface подразумевает, что массив строк (ролей) вернется

    либо отвяжите вашего юзера от UserInterface (я так и сделал), либо делайте так, как того требует интерфейс
    Ответ написан
    Комментировать
  • Как задать composer'у proxy в контейнере docker?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Вот корректный образ

    FROM php:8-fpm
    RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer


    Все собирается
    Ответ написан
    Комментировать
  • Как получить даты всех дочерних объектов бинарного дерева?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    У вас корректный код:
    sandbox.onlinephpfunctions.com/code/00ed97cf62c568...

    <?php
    
    class Node{
      private $arr = [];
      
      public function getChildren($node)
        {
    
            if (!empty($node->data)){
                array_push($this->arr, $node->data);
            }
    
            if (!empty($node->left)){
                $this->getChildren($node->left);
            }
    
            if (!empty($node->right)){
                $this->getChildren($node->right);
            }
    
            return $this->arr;
        }  
    }
    
    $node =  createNode(
        6 . '_base', 
        createNode(7 . '_right'),
        createNode(8 . '_left',
            createNode(81 . '_left'),
            createNode(82 . '_right',
               createNode(821 . '_right', 
                    null,  
                    createNode(824 . '_left') 
               )
              
            )
        )
    );
    
    function createNode($data, $right = null, $left = null) {
        $obj = new stdClass();
        $obj->data  = $data;
        $obj->right  = $right;
        $obj->left  = $left;
        return $obj;
    }
        
    var_dump((new Node)->getChildren($node));
    
    // [
    //  "6_base"
    //  "8_left"
    //  "82_right"
    //  "821_right"
    //  "824_left"
    //  "81_left"
    //  "7_right"
    // ]
    Ответ написан
  • Какие будут связи между сущностями?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Вы проектируете от связей в БД
    У вас схема показывает, что все связано со всем... это плохой подход, обычно проектируют от бизнес-процессов и уже в рамках них находят сущности и связи...

    И да, некоторые сущности связаны с другими только по id, но в БД не вяжутся, да, это не так жестко и связано, как на уровне БД повязать все через FK, но удобнее и проще (опыт десятилетий подходов и болячки больших проектов говорят об этом)

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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Генератор — просто инструмент для помощи, по итогу сущности чисты, не считая аннотаций/атрибутов для маппинга в ORM, но это просто мета-информации и завязка не существенна (не считая маленького компромисса с ArrayCollection). То есть если вы выберите др ORM, то эти аннотации вам не помешают никак, просто лишние заюзанные классы аннотаций

    Имея сущности доктрины — у нас не связанный от фреймворка код, пишите спокойно бизнесуху, не обращая внимания на то, как оно потом маппится. То есть практически все по каннонам

    Чтобы отделить репозиторий от домена — просто в домене делайте интерфейс, а вот реализация этого репозитория будет в Infrastrucure Layer, но это избыточно... риск минимальный, если сделаете не совсем по канону, а именно риск стоит как основной аргумент такового отделения (не просто же вы словам следуете, а причину понимаете?)
    Разработка строится на компромисах, если смените доктрину на др ORM — так и так писать новые репозиторий, вероятность низкая и многие например не делают такие интерфейсы — слишком усложнит код...
    Вам надо будет просто репозиторий в маппинге ORM\Repository заменить в таком случае

    Некоторые компании пишут интерфейсы и сильно усложняют код, но тк риск минимальный изменений, то такое усложнение приводит как правило к усложнению и не более. Следуйте здравому смыслу.
    Мой довод нельзя раскручивать "ну раз минимальный, то завяжусь по полной". Все же отделение логики надо делать

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

    617588c41bdde421641847.png
    Ответ написан
    8 комментариев
  • Мне 39 лет, возможно ли с нуля изучив php найти работу?

    Maksclub
    @Maksclub Куратор тега Карьера в IT
    maksfedorov.ru
    Как стать Java разработчиком за 1,5 года
    Меня зовут Сергей, мне 40 лет и сейчас я работаю Java разработчиком
    Ответ написан
    1 комментарий
  • Как инициализировать маршрутизатор Symfony в проекте?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Вам надо поставить еще пару пакетов:
    symfony/config
    symfony/cache
    doctrine/annotations


    по сути с конфигом вы почти симфони заведете в стандартной установке, не считая Request/Response из Symfony Foundation... уже тогда симфу и ставьте

    можете взять пакеты попроще, типа Aura/Slim, там при регистрации приложения просто добавляются новые роуты
    Ответ написан
  • Как лучше переадресовывать юзера при аутентификации?

    Maksclub
    @Maksclub Куратор тега Веб-разработка
    maksfedorov.ru
    Каждый запрос просто делайте с заголвоком Bearer: b43hb54b64bv46v46v464bmkkm
    Сервер прочитает, если нет токена, то 401 и отправить на страницу входа
    На входе получаете токен и сохраняете локально в localStorage, потом на каждом запросе к бекенду добавляете этот токен

    Такая типовая практика

    пишу все сам без фреймворка

    А это зря, уже все есть в фреймворках для удобной работы... например в Ангуляре просто в интерцепторе http запросов ловите каждый запрос и добавляете токен, если есть сохраненный в сторадже, могу предположить, что в др фреймворках также все есть
    Ответ написан
    Комментировать
  • Можно ли использовать сервисы в rich моделях?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    а? Если нет и нужно все равно писать абсолютно всю логику в обьекте модели, то тогда он станет просто god обьектом на 100500 строчек

    Потому что вы все делаете наоборот!

    Пишите по бизнес-процессу ваши UseCase, это некоторый хэндлер к примеру
    Пример
    final class HandleCheckOutShoppingCart
    {
        public function __construct(Carts $carts, PaymentGateway $gateway)
        {
            $this->carts   = $carts;
            $this->gateway = $gateway;
        }
    
        public function __invoke(CheckOutShoppingCart $command) : void
        {
            $shoppingCart = $this->carts->get($command->shoppingCart());
    
            $payment = $this->gateway->captureCharge($command->charge());
    
            $shoppingCart->checkOut($payment);
        }
    }


    Потом у вас вырисовываются границы сущности, и в сущностях уже инварианты, которые контролирует эта сущность. Никаких god object
    Ответ написан
    5 комментариев
  • Как дожидаться перехода по ссылке после click() без явного wait()?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Видели ли вы, что не со всем локаторами работает?

    It is important to understand that SmartWait works only with a specific locators:

    #locator - CSS ID locator, works
    //locator - general XPath locator, works
    ['css' => 'button''] - strict locator, works
    Ответ написан
    Комментировать
  • Есть ли особенности работы Doctrine в долгоживущих приложениях?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Как такое может быть?

    Если вы достаете сущность по id, то она загружается один раз и потом не обращается к БД, следя за изменениями сущности из приложения
    Предлагаю очищать EM после каждой обработки задачи через $em->clear()

    Sometimes you want to clear the identity map of an EntityManager to start over. We use this regularly in our unit-tests to enforce loading objects from the database again instead of serving them from the identity map. You can call EntityManager#clear() to achieve this result.
    https://www.doctrine-project.org/projects/doctrine...


    Чтение можно в обход делать, изменение сущностей лучше не делать в обход EM

    Консьюмеру не хватате параметра какого - то?

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

    Maksclub
    @Maksclub
    maksfedorov.ru
    Репозиторий может иметь методы find() — возвращает Optional
    а может иметь get() — возвращает или нужный объект или исключение, которе может лететь дальше или перехвачено контроллером и выброшено дальше

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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    у даты есть метод format()

    конкретно для вашего случая будет
    $date->format('c');

    https://www.php.net/manual/ru/datetime.format.php

    615eeda7241e0904013833.png
    Ответ написан
    Комментировать
  • Как изменить автогенерацию имени переменной с snake_case на camelCase?

    Maksclub
    @Maksclub
    maksfedorov.ru
    есть String plugin для таких изменений
    можно ли автоматом его запускать по файлам — хз, посмотрите

    https://plugins.jetbrains.com/plugin/2162-string-m...

    UPD: ааа, у вас дефолтные название в snake
    тогда наверное сюда615d980aad1ad628218126.png
    Ответ написан
    2 комментария
  • Чем отличается EntityManager->clear() от Doctrine->resetManager()?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Я вспомнил, течь в Доктрине может из-за SQL Logger, который в Connection
    Когда вы делаете resetManager() — вы убиваете и этот SQL Logger с его объектами в памяти

    Вам нужно сделать что-то типа
    $this->em->getConnection()->getConfiguration()->setSQLLogger(null);


    Это можно сделать на уровне настройки всего контейнера или сервиса, или в команде
    Но попробуйте для начала в своей консольной команде!
    Ответ написан
    Комментировать
  • Можете дать пример для использование паттерна декоратор?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Пример на псевдокоде

    UserProvider userProvider = new UserProviderAggregate(
         new LoggerUserProviderDecorator(
              new CacheUserProviderDecorator(
                  new UserProvider(...)
                  new Cache(),
             ),
             new Logger(),
         ),
         new DummyUserProvider()
    )


    все классы UserProviderAggregate, LoggerUserProviderDecorator, CacheUserProviderDecorator, DummyUserProvider и UserProvider реализуют UserProviderInterface... по сути представляют из себя реализации этой абстракции

    Просто каждый передает внутренней зависимости этого же типа (UserProviderInterface) работу внутри своей реализации метода этой абстракции

    public function loadUsers(): Collection<User> {
         return new Collection<User>(...);
    }


    или cache:
    public function loadUsers(): Collection<User> {
         if(cache.has('cache_users_key')) {
            return cache.get('cache_users_key');
        }
    
        Collection<User> users = new Collection<User>(...)
    
        cache.put('cache_users_key', users)
    
        return users;
    }


    или агрегированные:
    public function loadUsers(): Collection<User> {
         if(...any condition) {
             return dbUsers.loadUsers();
         }
    
         if(...any condition) {
             return dummyUsers.loadUsers();
         }
    
         throw new Exception();
    }
    Ответ написан
    Комментировать