• Как правильно писать глубокие интеграционные тесты?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Эти тесты функциональные строят обычно на фикстурах:
    - загружается состояние системы (например БД) до необходимого, например загружается категория КА и товар ТА
    - поднимается приложение или достаточное число сервисов для теста
    - проводится тест (например добавляется товар дублер товар ТА и система должна ругнуться или удаляется товар, которого нет, например товар ТБ)

    И так для каждого кейса — для него подгоняется состояние. Необходимо, чтобы тест все же должен быть относительно изолированным. Для некоторых тестов состояние одно и инструкции его поднятия будут одинаковы. Кроме того это быстро и проще.
    Для скорости тестов обычно берут in memory БД — это еще одна важная причина использовать сложные хорошие ORM, которые могут работать и с примитивными базами в памяти и с полноценными, отделяя код и работу с данными. Разница во времени по сравнению с реальным БД сильно выше.

    Вообще эти тесты сложные и потому их число как правило сильно меньше простых юнитов... Кроме того иногда уже имеет смысл делать это через приемочные (браузерные) тесты
    Ответ написан
  • Зачем в catch делать throw?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    try {
        $this->initDBConnect();
    } catch (\Exception $e) {
        // Тут залогировали
       $this->logger->error('Некоторое сообщение', [некоторые данные]);
    
        // тут выкинули дальше
        // или throw new MyAnyException, 
        // а не просто дальше выкидывать тоже самое
        throw $e; 
     }


    Для чего бросать дальше
    Например идет процесс оплаты товара, в клиенте выкинулось исключение... мы в слое работы с АПИ платежки поймали исключение и залогировали, но выкинули дальше, чтобы наша система выше поймала, откатила транзакцию и плюнула ошибкой уже своей для слоя выше
    Ответ написан
    3 комментария
  • В каких случаях при разработке сайта на PHP лучше использовать не MVC (и его подобия), а какой-нибудь другой подход?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Об архитектуре нужно обязательно почитать Робкрта Мартина: Чистая архитектура
    Ответ написан
    Комментировать
  • Что такое extra в Composer json?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Это дополнительные данные для команд (хуков) в секции scripts, в данном случае некий класс

    Чтобы понять эту тему — изучить нужно этот раздел: Composer Scripts
    Там будет про Event Handler и сами Events, через который и получают в своих обработчиках эти данные из extra через метод:
    $extra = $event->getComposer()->getPackage()->getExtra();

    В данном случае передается класс, может быть скаляр или json-объект, да что угодно в рамках синтаксиса и языка
    Ответ написан
    1 комментарий
  • Как в связке nginx+php71-fpm+yii настроить очередь запросов?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    и отдает он очередью именно этому клиенту, если параллельно буду заходить на сайт с другого окна из режима инкогнито у него своя очередь и страницы будут открываться

    Похоже на то, что к файлу сессии параллельно не может доступ получить другой процесс. Обычно для решения похожих проблем сессии выносят в тот же Redis.
    Но и тут могут быть проблемы: https://habr.com/ru/post/318836/
    Ответ написан
    1 комментарий
  • Php микрофреймворк - это rad фреймворк или enterprise?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Микройфреймворки для микро-сервисов, для API: для небольших сервисов

    Это могут быть и интерпрайз и rad, вообще вы можете придумать свою классификацию :)
    Классификация такая штука — для нее нужен признак, по которой классифицируют. Микро — с ограниченным функционалом, тк остальное выбирается под нужные задачи...

    нам же ничего не мешает подключить доктрину и прочие компоненты и перейти к enterprise

    Интерпрайз не зависит от наличия Доктрины. Интерпрайз — это про долго, давно, сложно и дорого.
    Ответ написан
    4 комментария
  • Как остановить {foreach} в шаблонизаторе Fenom?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Есть подозрение, что вы передаете в шаблонизатор лишние данные... Может поправить и передать ровно столько, сколько нужно?

    А так для вашего fenom будет так:
    {foreach $options as $option index=$index}
         {if $index == 5}
              {break}
         {/if}
    {/foreach}

    Источник: https://github.com/fenom-template/fenom/blob/maste...
    Ответ написан
    7 комментариев
  • Как задавать дополнительные свойства сущностям в Doctrine ORM?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Теперь я использую Doctrine ORM, у которой свойства должны быть закрытыми, а сеттеры/геттеры для таких вещей (которые в базу не заносятся, только контроллер и вид) не надо делать.

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

    Как выводить нужные в другом слое данные, которые нужны для отображения, а не для бизнес-логики — DTO,
    отвечал на днях: Что делать, если нужно получить часть данных сущности?

    Если хотите работать с полем сущности, но не хотите чтобы оно загружалось из/в БД, то просто не делайте для этого поля аннотацию (или не указывайте его в yml, если используейте его для маппинга):

    @Column
    Marks an annotated instance variable as "persistent". It has to be inside the instance variables PHP DocBlock comment. Any value hold inside this variable will be saved to and loaded from the database as part of the lifecycle of the instance variables entity-class.
    https://www.doctrine-project.org/projects/doctrine...


    Итого:
    Рекомендую или DTO или сервис (как указал BoShurik), который вернет ссылку, при том в этот сервис можно скормить роутер и создавать сылку не по названию сущности, а по названию роута — это облегчит потом смену сразу всех роутов одного типа, если не меняете название. А вообще и DTO и сервис нужны. В любом случае сущность знать о роутах ничего не должна, тем более этих роутов под одну сущность можеть быть куда больше, чем /blog/ и меняться это может довольно часто...
    Ответ написан
    Комментировать
  • Что делать, если нужно получить часть данных сущности?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Для отображения используйте DTO и доставайте их из репозитория. Для логики отображения негоже сущности из бизнес-слоя вытягивать.

    1 способ — заюзать Доктрину
    сразу не увидел, что вы без нее работаете

    Доктрина умеет создавать дтошки из коробки
    class PostDTO
    <?php
    
    namespace App\DTO;
    
    class PostDTO
    {
        /** @var string */
        private $name;
        
        /** @var string */
        private $description;
        
        /** @var string */
        private $text;
        
        public function __construct(string $name, string $description, string $text)
        {
            $this->name = $name;
            $this->description = $description;
            $this->text = $text;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function getDescription()
        {
            return $this->description;
        }
    
        public function getText()
        {
            return $this->text;
        }
    }

    И используя синтаксис NEW DQL создаем наши DTO :
    $query = $em->createQuery('SELECT NEW App\DTO\PostDTO(p.name, p.description, p.text) FROM App\Entity\Post p');
    $users = $query->getResult(); // array of PostDTO

    Источник: https://www.doctrine-project.org/projects/doctrine...

    2 способ — заюзать DBAL/PDO
    Сделать запросы через более низкую прослойку без ORM (например через Doctrine DBAL или PDO) и результат фетчить в те же самые PostDTO в репе
    Ответ написан
    2 комментария
  • Как выполнить несколько методов одним разом python?

    Maksclub
    @Maksclub
    maksfedorov.ru
    пара способов

    ЦЕПОЧКА ВЫЗОВОВ
    В коде вы привели пример, как хотели бы использовать: caller.hello().world()

    Если хотите, чтобы последний метод возвращал результат, то можно так:
    class Hello(object):
        def __init__(self):
             self.msg = 'Hello'
    
        def hello(self, world):
            self.msg = self.msg + ', ' + world
            return self
    
        def print(self):
            return self.msg
    
    caller = Hello()
    msg = caller.hello('niriter').print()
    print(msg) # Hello, niriter

    Такого рода код используется в разного рода билдерах, когда нужно настроить обьект разным способом по разным условиям (разного рода билдеры, в ОРМ запрос собрать и прочее)

    В вашей библиотеке по работе с html такой способ и используется (наряду со вторым) — вызываете разного рода методы, а возвращает все тот же обьект с разным состоянием и вы всегда можете вызвать новый метод (удалить элемент, добавить, все стереть) и по итогу вызвать получение своего нужного HTML

    ИНКАПСУЛЯЦИЯ

    class Hello(object):
        def __init__(self, msg='Friend'):
             self.msg = msg
    
        def print(self):
            init_msg = self.hello()
            return init_msg + ',  ' + self.msg
        
        def hello(self): # этот метод вызываем изнутри другого метода
            return str("Hello")
    
    caller1 = Hello()
    print(caller1.print()) # Hello, Friend
    
    caller2 = Hello('Maks')
    print(caller2.print()) # Hello, Maks


    Инкапсуляция из мира ООП, наружу предоставляет некий метод, и вызвав его — выполнится некоторое поведение и изменение состояния. Нужна для сокрытия всех деталей и нюансов и оставляет только удобное АПИ для работы.

    В вашей библиотеке по работе с html такой способ и используется (наряду с первым) — прячет сложные алгоритмы извлечения элементов, манипуляции с ними, отдавая назад только удобное АПИ с понятными названиями
    Ответ написан
    3 комментария
  • Как в symfony создать сервис, у которого зависимость определяется окружением?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Вариант самый простой и удобный от BoShurik — регистрация сервисов для конфигов под нужное окружение

    Если у вас это все в бандле происходит, то:
    ВАРИАНТ — ФАБРИКА

    Использовать фабрику и с ее помощью регистрировать ваш сервис.
    Фабрика уже сама создаст сервис с нужной зависимостью. Можете в фабрике сами зависимости для вашего сервиса занести например в приватную переменную или регистрировать как-то более хитро — просто их регать через Service Method Calls — мой рекомендуемый вариант.

    Итого — рекомендуемый и готовый код (неймспейсы поменять):
    // Класс, который содержит ваши сервисы под разные окружения
    class MyServiceDependencyRegistry
    {
        private $dependencies = [];
        
        public function register(DependencyInterface $dependency, string $env)
        {
            // Тут всякие проверки можно сделать
            // (особенно, что константа допустима)
            $this->dependencies[$env] = $dependency;
        }
        
        public function getDependency(string $env): DependencyInterface
        {
            // Тут проверочки
            // особенно, что константа допустима и есть с такой константой сервис
            // иначе исключение
            return $this->dependencies[$env];
        }
    }
    
    class MyServiceFactory
    {
        private $dependencies;
    
        public function __construct(MyServiceDependencyRegistry $dependencies)
        {
            $this->dependencies = $dependencies;
        }
    
        public function createNewService(string $env)
        {
            $envDependency = $this->dependencies->getDependency($env);
    
            // Если у вашего сервиса больше зависимостей
            // то вам нужно их передать в фабрику и тут уже передать в ваш сервис
            return new MyService($envDependency);
        }
    }
    
    
    
    # Регаем ваши зависимости (если сервисы  — они должны быть зареганы) 
    # Помним, что сами значения ключей (окружения) лучше константой использовать
    App\Email\MyServiceDependencyRegistry:
        calls:
             - [register, ['@App\Email\RealDependency', 'prod']]
             - [register, ['@App\Email\FakeDependency', 'test']]
    
    # Регаем фабрику, которая принимает в себя сервис, 
    # который отдаст вам  зависимости по ключу
    App\Email\MyServiceFactory:
       arguments: 
          - '@App\Email\MyServiceDependencyRegistry'
    
    # Регаем ваш сервис, указав фабрику и ее метод,
    # который в зависимости от переменной создаст нужный инстанс
    App\Email\MyService:
       factory:   ['@App\Email\MyServiceFactory', createNewService]
       arguments: 
          - '%env(APP_ENV)%'
    Ответ написан
    5 комментариев
  • Модели в паттернах MVC/MVP?

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

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

    Примерно как-то так:
    class WorkerRepository(object):
         def __init__(self, DBSession)
    
         def get_dismissed_count(self, count)
    
         def get_all(self)


    Пример: jordifierro.com/django-clean-architecture
    Тут примеры на Джанго, но на самом деле архитектура приведена из книги Роберта Мартина и относится и архитектуре десктоп-приложений, особенно присмотритесь к тому, как там организованы репозитории
    Ответ написан
    4 комментария
  • Преобразование путей при работе с архивами, какие есть варианты?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Используйте 7z, он конвертирует обратный слеш в нормальный
    https://help.ubuntu.ru/wiki/7zip
    Ответ написан
  • PHPUnit: можно ли одновременно замокапить и класс, и интерфейс?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Не надо плодить классы. Воспользуйтесь тогда анонимным классом (для таких задач их и придумали) прямо в коде:
    new class implement YourInterface {
        // тут методы, которые интерфейс содержит
        // тут методы, которые вы будете вызывать
    }
    Ответ написан
    8 комментариев
  • Как получить значения одной колонки из ArrayCollection в Symfony?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Вам нужно вызвать метод map(), и передать в него анонимную функцию, которая принимает сущность и возвращает ее id. По итогу вы получите коллекцию id этих сущностей

    https://www.doctrine-project.org/api/collections/l...

    Если очень большой объём, то чтобы туда-сюда массивы не генерировать — используйте \Generator, и уже по факту итерации берите id сущности и работайте с ним
    Ответ написан
    2 комментария
  • Что здесь лучше использовать? Простое наследование, интерфейсы, или вообще абстрактный класс?

    Maksclub
    @Maksclub
    maksfedorov.ru
    куча мест где выводится некий Список объявлений

    все они являются Списками, и эти списки по сути отображают одинаковые карточки

    Использовать DTO списка объявлений (или массив), который состоит из списка DTO объявления. Только 1 по сути класс для отображения
    Ответ написан
    6 комментариев
  • Можно ли Композицию и наследование назвать паттерном?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Паттерн — довольно утилитарная вещь, некая четкая последовательность или описание связей, чтобы можно было повторить, легко опознать и донести тот или иной инструмент.

    Композиция — абстрактное понятие, классифицирующая то, что нужно выстроить как-то иначе, нежели наследование. Делегировать и выстроить так, чтобы полиморфизм проявил себя через некоторое число объектов, не связанных в цепочку наследования.
    Наследование —про то, что объекты наследуют интерфейс/состояние, тут нет схемы взаимодействия, она довольно прямая: А от Б отнаследовался и все. Но внутри сокрыто много всего.
    Ответ написан
    Комментировать
  • Как преобразовать строку с значением "0"/"1" в булево значение false/true?

    Maksclub
    @Maksclub
    maksfedorov.ru
    import java.util.*;
    import java.io.*;
    
    public class Maksclub
    {	
        public static String STRING_TRUE = "1";
        public static String STRING_FALSE = "0";
    
        public static void main (String[] args)
        {
            String strFalse = "0";
            String strTrue = "1";
            
            System.out.println(isTrueBinaryString(strFalse)); // false
            System.out.println(isTrueBinaryString(strTrue));  // true
            System.out.println(isTrueBinaryString(null));  // IllegalArgumentException
            System.out.println(isTrueBinaryString("5"));  // IllegalArgumentException
        }
    	
        public static Boolean isTrueBinaryString(String value)
        {
            if (!STRING_FALSE.equals(value) && !STRING_TRUE.equals(value)) {
                 throw new IllegalArgumentException("Value must be `0` or `1`.");
            }
    	    
            return STRING_TRUE.equals(value);
        }
    }
    Ответ написан
  • Как делается правильная выборка данных со связанных таблиц БД при использовании JPA?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Для удобства можете использовать Criteria API, по сути сможете собирать запросы довольно близко к SQL, но манипулируя своими сущностями и их свойствами, добавляя аргументы и условия сборки.

    Примеры:
    - LEFT JOIN: https://www.logicbig.com/tutorials/java-ee-tutoria...
    - INNER JOIN: https://www.logicbig.com/tutorials/java-ee-tutoria...

    Приджойните нужные сущности, определите как выбирать те или иные данные. И такие выборки можете делать удобными, через нужные условия в своем репозитории.
    Ответ написан