Ответы пользователя по тегу PHP
  • Как оставить в ассоциативно массиве только определенные ключи?

    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...
    Ответ написан
  • Скажите как вам мой код?

    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

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

    это основное... к код-стайлу пока претензии не имеют смысла
    Ответ написан
  • Есть ли готовые компоненты для 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, с заполненными свойствами нужными нам значениями. Свойства приватные
    Создавать через конструктор не вариант по своим причинам.

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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    <?php
    
    $json = <<<JSON
    {
      "issuance": {
        "1": {
          "data": "01.20",
          "quantity": 2
        },
        "2": {
          "data": "01.20",
          "quantity": 4
        },
        "3": {
          "data": "04.20",
          "quantity": 2
        },
        "4": {
          "data": "04.20",
          "quantity": 2
        }
       }
    }
    JSON
    ;
    
    $arr = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
    
    $res = new \stdClass();
    $res->issuance = array_values(
        array_reduce(
            $arr['issuance'],
           function ($res, $el) {
                $res[$el['data']]['data'] = $el['data'];
                $res[$el['data']]['quantity'] = isset($res[$el['data']]['quantity'])
                    ? $res[$el['data']]['quantity'] + $el['quantity']
                    : $el['quantity'];
    
                return $res;
            },
            []
        )
    );
    
    var_dump(json_encode($res, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, 512));

    sandbox.onlinephpfunctions.com/code/c5986c6fd8244a...

    * JSON_THROW_ON_ERROR — этот флаг появился только в 7.3, если версия меньше — убрать
    Ответ написан
  • Почему дефолтные параметры методов бесполезны?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    передавать значение по . дефолту + использовать константы

    public const DEFAULT_VAR = 4444;
    public const DEFAULT_VAR_2 = 55555;
    
    public function test(?int $var = self::DEFAULT_VAR, ?int $var2 = self::DEFAULT_VAR_2){
        echo $var;
        echo var2;
    }
    
    ...
    
    $obj->test(Obj::DEFAULT_VAR, 9999);

    Если прям нужно что-то сложное, то аргументом могут статькак DTO, так и ValueObject (в зависимости от контекста)
    public function test(AnyType $anyType) {}
    
    ...
    
    $obj->test($valueObject);

    у которого дефолтные значения есть и который вы можете настроить нужным образом

    Если какой-то сложный случай, то скорее всего надо разбивать дальше, например на один обязательный и второй как некий конфиг/расширенные настройки или некими др эвристиками. Все можно сделать красиво и немногословно. Nullable аргументы — для меня плохой запах и я предпочитаю Null Object, понятное дело по ситуации.
    Если речь пошла про Dto, то можно сделать примерно так:
    class OrderDto::__construct(ind $id, DateTimeImmutable $createdAt, array $products = []) {}
    class UserDto::__construct(ind $id) {}
    class OrderDetailsDto::__construct(OrderDto $userDto, ?UserDto $userDto = null) {}


    В случае однотипных объектов можно так:
    class SettingsDto::__construct(SettingDto...$settingDto) {}
    ...
    // использовать тогда так:
    new SettingsDto(
        new SettingDto(1, 'one'),
        new SettingDto(2, 'two'),
        ...,
        new SettingDto(n, 'n')
    )

    То есть в одном объекте настроек можно передавать объекты конкретных настроек
    Ответ написан
  • Зачем писать два слэша?

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

    5e0d2dcd61e13770335616.png
    https://www.php.net/manual/ru/language.types.string.php

    Напоминание:
    Когда будете использовать регулярные выражения в переменных, а точнее обратный слэш, то это особо пригодится:
    '\\d+'
    Ответ написан
  • Как реализовать облачное хранилище?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Для облачного хранилищ нужно облако (логично, судя по названию)
    Потому нужно создать это самое облако -- начните с OpenStack (на вики)
    PHP SDK: https://github.com/php-opencloud/openstack

    Посмотрите в сторону Swift (на вики)из OpenStack
    https://www.openstack.org/software/releases/train/...

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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Дата рождения в сочетании с датой текущей - есть возраст.
    27 декабря родился, значит сейчас 4 годика :) и зачислять ничего не нужно, разница просто есть.

    Работа с датой из коробки во всех языках и базах данных.

    Возраст: 0.
    Через 24 часа:
    Возраст: 1/2/и так до бесконечности.
    Нет, не в годах/etc, а именно чтобы просто цифра увеличилась в строке.
    P.s: извините, забыл упомянуть, что "лет" годы" и прочее просто слово.

    Мы вас поняли, об этом и говорим! В коде ниже поменять маску формата, и возраст будет не днями, а чем-то другим, что зададите, (%a - число дней)
    Пример на коленке: (без проверок, образец)
    <?php
    
    class Person
    {
        /** @var  \DateTimeImmutable */
        private $registeredAt;
    
        /** 
         * Тут число дней как значение, но можете число лет, месяцев, как вам угодно
         * @var string 
         */
        private $ageFormat = '%a';
        
        public function __construct()
        {
            // тут для примера зашил дату декабря прошлого года для наглядности, 
            // в реале значение даты рождения из БД будет браться
            $this->registeredAt = new \DateTimeImmutable('2018-12-28');
        }
        
        public function getAge(): int
        {
            $now = new \DateTimeImmutable();
            
            return (int) $this->registeredAt->diff($now)->format($this->ageFormat);
        }
    }
    
    $person = new Person();
    
    // 368 лет (дней) на 31 декабря 2019 года по отношению к "дате рождения"  2018-12-28
    // 1 января 2019 года будет 369
    // 2 января 2019 года будет 370
    // ...
    // крч, всегда отдаст твой "возраст" актуальный в нужных попугаях
    echo $person->getAge();


    Более подробно про дату: https://www.php.net/manual/ru/class.datetimeimmuta...
    Про маски для формата diff: https://www.php.net/manual/ru/function.date-diff.php
    Ответ написан
  • Как при выполнении Carbon->addDays(int) не обновлять состояние?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    We also provide CarbonImmutable class extending DateTimeImmutable. The same methods are available on both classes but when you use a modifier on a Carbon instance, it modifies and returns the same instance, when you use it on CarbonImmutable, it returns a new instances with the new value.

    https://carbon.nesbot.com/docs/


    CarbonImmutable при изменении данных создает новый обьект, не меняя старый. Так как работает на основе DateTimeImmutable:
    https://www.php.net/manual/ru/class.datetimeimmuta...
    Ответ написан
  • Как добавить в ассоциативный массив значение attributes() эллемента xml?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Когда вы превращает документа в SimpleXMLElement, то каждая дочернаяя нода тоже таковой становится и у нее также есть свой набор атрибуттов $element->attributes()
    Итоговый код

    <?php
    
    $string = <<<XML
    <offer id="130" available="true" >
    <url>http://api.loc/index.php?route=product/product&amp;path=3899&amp;product_id=130</url>
    <price>850</price>
    <currencyId>UAH</currencyId>
    <categoryId>3899</categoryId>
    <delivery>true</delivery>
    <stock_quantity>100</stock_quantity>
    <name>Босоножки 82BLUE р. 27 17,5 см Синий</name>
    <vendor>(производитель не указан)</vendor>
    <barcode>200</barcode>
    <param name="Размер обуви">46</param>
    <param name="Размер стельки">19</param>
    </offer>
    XML
    ;
    
    $xml = new SimpleXMLElement($string);
    
    $params = [];
    // Все ноды с тегом param ушли в массив, каждый из из которых  SimpleXMLElement
    foreach($xml->param as $param) {
        // а также @attributes, в котором все атрибуты текущего элемента, достаем тот, что name
        $paramName = $param->attributes()['name'];
        $params[(string) $paramName] = (string) $param;
    }
    
    var_dump($params);
    // [
    //     "Размер обуви"   => string(2) "46"
    //     "Размер стельки" => string(2) "19
    // ]


    UPD: Чтобы руками все не писать и не гадать, какой параметр пришел каким по счету (а могут прийти в одной выгрузке в разном порядке из-за того, что у одного товара просто не указан один из параметров, то предлагаю сделать так:

    1. Завести карту всех соответствий
    $featuresMap = [
        'Размер обуви' => 'size',
        'Размер стельки' => 'stelka',
    ];

    2. И уже динамически собирать с каждого элемента по этой карте параметры
    foreach($xml->param as $param) {
        $paramName = $param->attributes()['name'];
        $paramAlias = $featuresMap[(string) $paramName]
        $params[$paramAlias] = (string) $param;
    }


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

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Дженерики еще не подвезли, которые бы сделали это возможным
    Используйте коллекции (из Симфони или Ларавел возьмите), если хотите, чтобы был некий контроль за типом, но и они, опять же, не будут полноценно контролировать тип элемента сами по себе.

    class Bar
    {
        /** @var ArrayCollection|Bar[] */
        public ArrayCollection $props;
    }


    Для решения вашей задачи нужен еще один виток развития языка :)

    НЕсуществующий код!
    class ArrayCollection<T>
    {
        public getElement(): <T>;
        public addElement(<T> $element): void;
    }
    
    class Bar
    {
        public ArrayCollection<Bar> $barCollection;
    }


    Единственное, можно свои коллекции прокачать на проверку типа при создании объекта этой коллекции
    Ответ написан
  • Что за Undefined offset?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    У вас дампится все отлично — если увидите, на скрине вверху есть слово login
    Очевидно код срабатывает еще раз, там где нет метода и крашится, тк в новый раз этих данных нет...

    Чтобы это узнать, перед получением $s сделайте:
    dd(get_class($event->getController()));
    И покажите полный вывод ВСЕХ значений...

    Вообще потратьте время и поставьте xDebug, вы бы все поняли в течение 2 секунд что и куда прилетает и почему...

    Если выбрали дебаг как раньше на коленке, то у симфы есть функция dump(), все вызовы которой выводятся в панеле разработчика!
    Ответ написан
  • Как спарсить этот JSON?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    <?php
    
    $jsonProducts = '[
      {
        "market_name": "T-shirts",
        "size": "M"
      },
      {
        "market_name": "T-shirts",
        "size": "L"
      }, 
      {
        "market_name": "Jacket",
        "size": "M"
      }, 
      {
        "market_name": "Jeans",
        "size": "XL"
      }
    ]';
    
    $jsonPrice = '{
        "data": [
            {
              "updated_at": 1576048969000,
              "prices": {
                "last": 5300
              },
              "name": "Jacket"
            },
            {
              "updated_at": 1576048969000,
              "prices": {
                "last": 2000.12
              },
              "name": "T-shirts"
            }
        ]
    }';
    
    $products = json_decode($jsonProducts, true);
    $price = array_column(json_decode($jsonPrice, true)['data'], 'prices', 'name');
    
    $result = array_map(function($item) use ($price) {
        $product              = new \stdClass();
        $product->market_name = $item['market_name'];
        $product->size        = $item['size'];
        $product->price       = $price[$item['market_name']]['last'] ?? null;
        
        return $product;
    }, $products);
    
    $result = json_encode($result, JSON_PRETTY_PRINT);
    
    var_dump($result);
    // [
    //     {
    //         "market_name": "T-shirts",
    //         "size": "M",
    //         "price": 2000.12
    //     },
    //     {
    //         "market_name": "T-shirts",
    //         "size": "L",
    //         "price": 2000.12
    //     },
    //     {
    //         "market_name": "Jacket",
    //         "size": "M",
    //         "price": 5300
    //     },
    //     {
    //         "market_name": "Jeans",
    //         "size": "XL",
    //         "price": null
    //     }
    // ]


    sandbox.onlinephpfunctions.com/code/935656788a07af...
    Ответ написан
  • DI: как инстанциировать реализацию интерфейса с кастомным значением параметра конструктора?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Используйте фабрику:

    - В конфиге контейнера
    // регайте ваш интерфейс, но ссылаясь на метод фабрики,
    // которая и произведет ваш сервис
    \App\Writer\WriterInterface::class => DI\factory('\App\Writer\TextWriterFactory::create'),

    - Код фабрики:
    class TextWriterFactory implements WriterFactoryInterface
    {
        public function __construct(Settings $settings, DataConverter $dataConverter)
        {
            $this->settings = $settings;
            $this->dataConverter = $dataConverter;
        }
    
        public function create(string $filePath): WriterInterface
        {
            return new TextWriter($this->settings, $this->dataConverter, $filePath);
        }
    }


    Источник: php-di.org/doc/php-definitions.html#factories
    Ответ написан
  • Как переписать preg_replace_callback()?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Callback можно задавать как
    Как массив объекта и его метода: [$obj, 'myCallbackMethod']
    Для статического метода: [MyClass::class, 'myCallbackMethod']
    Другой способ для статического метода: 'MyClass::myCallbackMethod'

    Пример для вас:
    <?php
    
    class TestListener
    {
      //   сигнатуру тут лишнюю для простоты примера просто убрал
      public function onContentPrepare(&$row) {
    
        $row->text = preg_replace_callback('|{gold}|', [$this, 'insertgold'], $row->text);
      } 
    
      public function insertgold(array $matches)
      {
          return $matches[0] . 'Called';
      }
    }
    
    
    // Example
    $string = new stdClass();
    $string->text = 'TestText with gold  and gold ';
    
    $listener = new TestListener();
    $listener->onContentPrepare($string); 
    var_dump($string->text ); //TestText with goldCalled  and goldCalled
    Ответ написан
  • Как разрулить конфликт зависимостей composer?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Если под 3 версию не хватает адаптеров, то нужно:
    • либо доработать адаптеры под 3-ю версию и внести в клад в опенсорс,
    • либо сделать форк 2 версии для самой библиотеки и для common, адаптировать common (форк) под работу с symfony/http-foundation 4 версии, выложить на Packagist и затянуть к себе уже свой форк


    Приходилось как-то пару раз мутыжиться со вторым способом... гемморно, но нужно было
    Может так статься, что адаптер форкнуть будет проще
    Ответ написан
  • Как асинхронно выполнить скирпт php?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Возьмите любой асинхронный фреймворк на php:
    RoadRunner
    ReactPHP

    С помощью него запускайте свой воркер
    И результат через веб-сокет отдавайте на клиент
    Ответ написан
  • Почему PHP требует Apache или Nginx чтобы работать в docker контейнере?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    какой смысл использовать Apachе или Nginx содержащий образ? Разве нельзя работать с контейнером содержащим только php?

    Разве нельзя работать с контейнером содержащим только php?

    Можете. Вы можете поставить PHP-CLI контейнер и работать с ним без веб-сервера конечно же
    FROM php:7.1-cli
    Что позволит вам запускать консольные команды (композер туда же)

    Если ставите PHP-FPM контейнер, то понятное дело вам нужен веб-сервер (гуглите принцип работы с Fast CGI).
    Нужен, если вы хотите общаться с приложением через HTTP
    FROM php:7.1-fpm
    Ответ написан