• Как конвертировать массив в php?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    <?php
    
    $array = ['name' => 'Albert1991'];
    
    // json
    var_dump(json_encode($array)); //{"name":"Albert1991"}
    
    // НЕ json, а сериализованный формат php
    var_dump(serialize($array)); // a:1:{s:4:"name";s:10:"Albert1991";}
    Ответ написан
    1 комментарий
  • Как добавить префикс к url?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Возможности, начиная с 3 версии: https://symfony.com/blog/new-in-symfony-3-4-prefix...
    Начиная с 4.1: https://symfony.com/blog/new-in-symfony-4-1-prefix...

    5 версия - это 20-40 мелких фич на сотни тысяч строк кода прошлых версий, а не другой фреймворк
    Ответ написан
    1 комментарий
  • Простенький SQL запрос ,как развязать?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Таблица clients
    first_name | last_name | age

    SELECT *
    FROM clients
    ORDER BY age DESC
    LIMIT 3
    Ответ написан
    3 комментария
  • Какой системой управления проектами Вы пользуетесь?

    Maksclub
    @Maksclub
    maksfedorov.ru
    На работе YouTrack — вполне нравится,
    но ооочень понравился в свое Яндекс.Трекер, когда работал в Яндексе — этот продукт не только внутри, но и на рынке (на 1-го человека вроде бесплатно)
    https://yandex.ru/tracker/
    Ответ написан
    2 комментария
  • Зачем нужна абстракция в ООП?

    Maksclub
    @Maksclub
    maksfedorov.ru
    В Python нет интерфейсов, потому приходится так объявлять абстракцию без поведения.
    Зачем нужна абстракция -- затем, чтобы код не завязывать на конкретный объект, а завязать на некоторые границы поведения (что и есть абстракция) -- в данном случае на некоторый SUbject
    class Observer(ABC):
        @abstractmethod
        def update(self, subject: Subject) -> None:
            """
            Получить обновление от субъекта.
            """
            pass

    Теперь Observer (все его потомки) в параметрах метода update() принимают ЛЮБОЙ объекта типа Subject, то есть код завязан на абстракцию, а не реализацию этих subjects и вообще ничего не знает о их названиях и индивидуальном поведении, знает только, что прилетит любая абстракция типа Subject....
    Ответ написан
    Комментировать
  • Куда пропадают потенциальные заказчики?

    Maksclub
    @Maksclub
    maksfedorov.ru
    У предпринимателя на ряду с версткой/программированием от фрилансера чего-то мелкого есть еще ряд проблем:
    - ФНС
    - другие фискальные службы и проверки
    - зарплата
    - клиенты
    - ремонт офиса
    - поставщики
    - партнеры
    - менеджеры по продажам звонят
    - подчиненные
    - собственно само дело
    - аренда
    - закрытие бизнеса из-за карантина
    - ваши конкуренты тоже пишут и звонят и могут это делать навязчиво, из-за чего лимит времени на данный фокус занятости может быть исчерпан

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

    Кроме того мы не знаем, с кем в общаетесь, как вы общаетесь и что за услуга.
    Ответ написан
    Комментировать
  • Как обнулить или объединить миграции и оставить одну?

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

    На машине разработчика
    Если все сделано аккуратно (когда работали с миграциями), то можно удалить все таблицы и попробовать сгенерировать diff — он сделает актуальную миграцию, которая необходима. Далее нужно удостовериться — не было ли придуманных вне ORM миграций и таблиц, крч сверить с боевой схемой. Также возможно миграции несли некоторые данные (ну например коды валют, которых всего 5-10 вы решили миграцией занести или пара дефолтных значений из таблицы settings, и подобные случаи) — нужно будет руками это повторить (или не повторять, если не нужно).
    Если вы добились, что ПУСТОЙ БД накаткой миграций получается ровно то, что достигалось ранее, то:
    • вы спокойно можете удалить старые миграции и коммитить эту одну большую
    • перед выкаткой на прод — вы должны текущую миграцию занести в таблицу migration_versions руками


    Done!

    Все делается очень легко, мне как-то достался проект без доступа к старому GIT (и почему-то без миграций), но с боевой БД. Пришлось проделать приблизительно все тоже самое (не считая болевого обновления Symfony с 2.7 до 3.4 с Сонатой, было больно :):):))...
    Все проделывания с миграциями заняли от силы полтора часа (в виду молодости и не опытности), было пару подводных моментов (типа у некоторых бандлов из vendor были свои папки со своими мииграциями) и данные нужны были. некоторые, а так все относительно легко
    Ответ написан
    Комментировать
  • Правильно ли размещать логику валидации в классе Request?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Ну да, не по SOLID код :) Если вы об этом
    https://github.com/laravel/framework/blob/7.x/src/...

    Нарушено сильно пару принципов:
    • Single Responsibility Principle
    • Liskov Substitution Principle - нельзя подменить FormRequest классом Request, там где идет завязка на индивидуальный для FormRequest интерфейс


    Но, к слову, это довольно удобный способ валидации :)
    Ответ написан
  • Должны ли Value Object быть Final?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Если статус не является одним из них - выбрасываем исключение. Так же было бы удобно в самом статусе создавать объект через методы фабрики: Status::active(), Status::publish().

    Такой тип с перечислением необходимых значений называетсяEnum, Enum можно считать VO
    Абстрактный функционал вашего псевдотипа, понятное дело, не будет финальным, но конкретные VO конечно делайте final и будет все хорошо.

    Единственное сложные VO трудновато будет тестировать с final модификатором, вам поможет например uopz расширение.

    Для статусов/ролей/id делать VO — круто и хорошо, делать final — тоже

    В догонку статья от Ocramius: When to declare classes final
    Ответ написан
  • Symfony, Argument resolver используется по назначению?

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

    Так неявно сделано в разных админ-панелях. Только не в аргументах контроллера, а просто в реквесте, в EasyAdmin так
    Ответ написан
  • Как оставить в ассоциативно массиве только определенные ключи?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    $data = [
        'вес'               => '',
        'дата'              => '',
        'телефон'           => '',
        'город'             => '',
        'товар'             => '',
        'суммазаказа'       => '',
        'остаток'           => '',
        'статусзаказа'      => '',
        'ок'                => '',
        'предоплата'        => '',
        'доставка'          => '24',
        'поставщик'         => '',
        'статусоплаты'      => '',
        'номерзаказа'       => '6666',
        'оплата'            => '',
        'ценазакупки'       => '',
        'прибыль'           => '',
        'артикул'           => '',
        'коментарии'        => '',
        'коментарийклиента' => '',
        'датазаказатовара'  => '',
        'статусдоставки'    => '',
        'магазин'           => '',
    ];
    $filterKeys = ['доставка', 'номерзаказа'];
    
    $result = array_filter($filterKeys, function($key) use ($data) {
        return isset($data[$key]);
    });
    
    var_dump($result);

    sandbox.onlinephpfunctions.com/code/7093a31de82bcb...
    Ответ написан
  • Какой паттерн использовать для задачи получение заказов, отправка статусов заказов из нескольких разных внешних источников?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Начинаем с границ
    Очерчиваем границы нашей системы, как-будто у нас есть этот "идеальный заказ".
    Это будут интерфейсы некоторых абстрактных реквестов и респонсов.
    Реквест на обновление, добавление и респонс на получение и т.д.... И несколько ДТО их реализующие или которые и будут этим интерфейсом сами по себе.

    Далее вы строите клиент OrderClientInterface, который выше созданные реквесты отправляет, респонсы возвращает. И на его интерфейс вы завязываетесь. Строите поверх него фабрику, которая построит вам нужный клиент под нужную систему :)

    Адаптеры (к слову это и паттерн одноименный)
    Клиенты-адаптеры уже будут связывать АПИ внешних систем с интерфейсом вашего абстрактного (имеется в виду интерфейс) клиента. Через guzzle, или через некий свой sdk, уже для вашего домена не важно, дело техники. С авторизацией или без нее... Это детали клиента.

    Это довольно общая рекомендация, но стоящая и очень упростит вам работу. Несколько интерфейсов, несколько дто, фабрика и остальное дело техники, просто и надежно

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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    function myFunc(...$args) {
        // вернем все аргументы, что пришли в функцию
        return $args;
    }
    
    $arr = [1, 'Maks', new \DateTime(), null];
    
    var_dump(myFunc(...$arr));

    sandbox.onlinephpfunctions.com/code/c5ba5d829c63a1...
    Ответ написан
    2 комментария
  • Скажите как вам мой код?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    require_once 'classes.php';
    Перейти на Композер и неймспейсы

    class smartPhoneProduct
    Оставить Product, а тип товара вынести в свойство товара type или в category

    Все, что касается модификаций — вынести в характеристики, например может подойтиEAV, то есть некая сущность Характеристика с product_id, name, value и таких быть может много у одного товара. Или иной способ работы, главное чтобы при добавлении сотен характеристик вам не пришлось бы сотнями файлов классы придумывать и всегда был один Product, ну или были др причины создавать детей, например простой товар и цифровой...
    Альтернативы EAV обсуждались например тут: Альтернатива EAV, структура базы?

    getProduct()
    После исправления предыдущего шага данная штука product->getProduct() устранится, а сериализация должна быть в другом слое, товар не должен знать ничего про то, как отображать его, тк отображений может быть много (в каталоге, в письме, в отчетах, в корзине)

    echo "\nСкидка: " .$discountOnProduct. "P";
    echo — вывод, бизнес-объект не должен ничего знать про вывод, ответ сервера и прочее, должен быть просто return $this->description()
    Тем более цена не должна идти в описании, а быть неким getPrice(), причем как правило это делают не у товара, а у вариантов/предложений товара, которые с товаром связаны 1 к 1

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

    это основное... к код-стайлу пока претензии не имеют смысла
    Ответ написан
    6 комментариев
  • Symfony - в чём разница между listener и subscriber, и что означает теговать (tags)?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Listener — слушатель одного или более событий, некий такой атомарный исполнитель. Не знает на что он подписан, но легко управляется через конфигурацию. Если разные слушает — нужно самому логику разделения обработки писать.
    Subscriber — подписчик имеет реализацию интерфейса SubscriberInterface с методом getSubscribedEvents(), то есть сам объект слушателя знает, на что он подписан и какие методы какие события будут отрабатывать

    По сути одно и тоже, но такая разница иногда пригождается. Иногда нужно иметь контроль снаружи, иногда наоборот.

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

    В коде самого фркймворка этот механизм активно используется. Например есть разного рода констрейнты - добавить новый вы можете и автоматом зарегистрировать, проставив тег (на самом деле симфони-разработчики пошли дальше и тег навешивают по интерфейсу, но важен механизм) и данный констрейнт будет доступен коду, в котором требуются все констрейнты (опять той же фабрике)
    Ответ написан
    Комментировать
  • Как создать собственный валидатор в symfony?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Я вроде читаю https://symfony.com/doc/4.4/validation/custom_cons... но чё то понять не могу.

    /**
    * @Annotation
    */
    class ContainsAlphanumeric extends Constraint
    {
    public function validate($value, Constraint $constraint) {}
    }


    Небольшая путаница произошла по спешке: аннотация (констрейнт) — это одно, валидатор — другое, они связаны, но это две разные вещи одной системы.

    Constraint — по сути выражение/аннотация, по которой будет срабатывать валидатор. Она принимает в себя некие параметры и содержит логику к чему применяться — к классу или филду класса.

    Создаем свой констрейнт:
    В нашем констрейнте мы планируем, что мы сможем передать некий дополнительный параметр размер, а если не передадим, то его значение будет 55 (ну так хотим). Причем это будет относиться не к кполю, которое валидируем, а именно к констрейнту, То есть то, что он отдаст валидатору. Ну например возраст или некий пинг,да что угодно, в общем ориентируясь на который валидатор применит некую логику.
    /**
     * @Annotation
     */
    final class AnyConstraint
    {
        /**
         * @Required
         * @var int
         */
        public $size = 55;
    
        /**
         * Вот тут мы укажем некий валидатор, тот класс, который и будет валидировать наше значение
         * Дял удобства он будет называться также + слово Validator, в нашем случае AnyConstraintValidator
         */
        public function validatedBy()
        {
            return \get_class($this).'Validator';
        }
    }


    Создаем собственно валидатор
    на который выше в методе validatedBy() мы уже указали

    class AnyConstraintValidator extends ConstraintValidator
    {
        public function validate($value, Constraint $constraint): void
        {
            // Тут логика, при которой или все пройдет до return - все отлично
            // Или вообще исключение вывалится (если пришло чего-то не то)
            // Или (для чего и используется валидатор) создастся violation
    
            // например число в объекте больше того, которое мы указали в size (по дефолту если не указали: 55)
            if($value > $constraint->size) {
                $this->context->buildViolation('Тут пинг пришел вообще большой  —  такой нельзя, брат')
                    ->setParameter('{{ string }}', $value)
                    ->addViolation();
            }
        }
    }


    Используем:
    class AnyEntity
    {
        /**
    
         * Если значение больше 55  — то не пройдет валидацию
         * @AnyConstraint(120)
         */
        protected $name;
    }


    Помимо всего у констрейнта можно указать к чему применять — к методу, классу или к переменной класса
    • Через аннотацию у класса констрейнта: @Target({"PROPERTY", "METHOD", "ANNOTATION"})
    • Через метод переопределенный метод getTarget()
    Ответ написан
    Комментировать
  • Как правильно организовать запись в бд, поле пригласил?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Тут не нужна другая сущность.

    One-To-Many, Self-referencing
    You can also setup a one-to-many association that is self-referencing. In this example we setup a hierarchy of Category objects by creating a self referencing relationship. This effectively models a hierarchy of categories and from the database perspective is known as an adjacency list approach.

    class User
    {
        /**
         * Кого пригласил
         * @var ArrayCollection
         * @ORM\OneToMany(targetEntity="User", mappedBy="invitedBy")
         */
        private $invitedMembers;
    
        /**
         * Кем был приглашен
         * @var User
         * @ORM\ManyToOne(targetEntity="User", inversedBy="invitedMembers")
         */
        private $invitedBy;
    
        public function __construct() {
            $this->invitedMembers = new \Doctrine\Common\Collections\ArrayCollection();
        }
    }
    Ответ написан
  • Есть ли готовые компоненты для Symfony (но можно и без привязки к нему), способные делать то, что делает Doctrine через рефлексию?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Прямой ответ на вопрос:

    1. Symfony Normalizer в составе Symfony Serializer
    https://symfony.com/doc/current/serializer/normali...
    А именно PropertyNormalizer:
    Symfony includes the following normalizers:
    ...
    PropertyNormalizer to normalize PHP object using PHP reflection.


    2. Zend Hydrator
    Дока: https://docs.zendframework.com/zend-hydrator/
    Код: https://github.com/zendframework/zend-hydrator

    Именно как Доктрина через рекфлексию, то это ReflectionHydrator
    Но в пакете есть и ряд других, не через рефлексию

    Например свежая Cycle ORM юзает именно этот пакет

    ..................................

    получили по API ответ от некоего сервиса, содержащий поля и их значения некоего объекта "Документ", и после на основании некоего конфига мы создаем новый инстанс класса Document, с заполненными свойствами нужными нам значениями. Свойства приватные
    Создавать через конструктор не вариант по своим причинам.

    А вот если прислушаться к вашей задаче, то что мешает создавать через фабричный метод (именованный конструктор)?
    Ответ написан
    1 комментарий
  • Что такое обратный вызов в программировании?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Некоторые случаи использования коллбека (помимо асинхронного упомянутого использования):

    В коде: отложенный (ленивый) вызов.
    Например есть некий контейнер, в него мы регистрируем некие объекты с некоторой логикой.
    Но так как очень много таких кандидатов и они все запускаются, замедляя и работу и нагружая ресурсы, то мы бы хотели, чтобы контейнер просто знал о них, но при инициализации не запускал весь код создания этих объектов. На помощь приходят коллбеки — в коллбеке мы описываем логику создания объекта и (внимание) регистрируем именно коллбек с этой логикой создания, все — это очень быстро и легко, тк не наплодили объектов. Далее только при вызове нужного объекта контейнер видит, что зареган коллбек и вызывает его, тем самым инициализирует создание нужного объекта, описанное в этом коллбеке! ПРОФИТ!

    В коде: рекурсивно применить некоторый функционал к элементам коллекции.
    Очень популярное использование в Java в стримах.
    Есть коллекция элементов, запускаем некий map(), reduce(), filter(). walk() по набору элементов (коллекции) передавая в этот обход коллбек, и ожидаем, что получим результирующий набор после применения коллбека к каждому элементу. То есть этот коллбек будет выполнени внутри обхода, сама конструкция этих методов подразумевает, что вы передадите что-то что потом вызовется.

    Между системами: асинхронное взаимодействие
    Hook, callback
    Есть программа, которая ждет событий от внешней системы. Например есть платежная система Яндекс.Касса и есть ваш магазин. Вы отправили клиента на оплату в платежку, но он ушел "в путешествие" и вы не можете узнать судьбу платежа сразу. Вы можете бомбить сайт платежки, проверяя статус по номеру транзакции, а можете дождаться обратного вызова (коллбека, хука) от платежной системы с событием "Оплатил" или "Не оплатил, тк не хватило".
    Также в др системах — само оповестит, вызвав обратно уже меня.
    Ответ написан
    3 комментария
  • Можете найти простой ответ на второй вопрос из собеседования?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Как сделать так, чтобы, получив обновлённое приложение, конкуренты не смогли узнать его новые функции?

    Фиче-свитчи Приложение может быть обновлено и быть с новыми фичами, но они будут отключены, причем выключатели находятся не на стороне клиента, чтобы могли конкуренты узнать или СМИ или не важно кто, а на стороне бекенда — каждый раз приложение будет запрашивать данный список. И управляя бекендом — можно тестировать, включать по релизу/событию.
    Кроме того, что никто не узнает, так еще и можно делать так, что фичи будут включаться на части пользователей, чтобы протестировать на боевых пользователей и выкатывать по мере уверенности на все большей и большей доле.
    Ответ написан
    9 комментариев