Задать вопрос
  • Symfony, Argument resolver используется по назначению?

    IgorPI
    @IgorPI Автор вопроса
    Для авторизованного доступа (например для изменения)
    0q4lfOm.gif

    Для публичного доступа (для просмотра)
    NAtiFAm.gif

    В полезной нагрузке идентификатор точки продаж.
  • Symfony, Argument resolver используется по назначению?

    IgorPI
    @IgorPI Автор вопроса
    Для упрощения понимания.

    Не авторизованные посетители видят все продукты во всех торговых точках.
    Авторизованные посетители видят продукты только в одной торговой точке.
    Администраторы видят продукты только если авторизованы и только в той торговой точке где получили токен.
  • Symfony, Argument resolver используется по назначению?

    IgorPI
    @IgorPI Автор вопроса
    То есть, вопрос, есть ли криминал в том что для каждой сущности свой резольвер?
  • Symfony, Argument resolver используется по назначению?

    IgorPI
    @IgorPI Автор вопроса
    Вот как реализован резольвер для Product

    <?php
    
    
    namespace App\ArgumentResolver;
    
    use App\Classes\RestCode;
    use App\Entity\Product;
    use App\Exception\ArgumentResolverException;
    use App\Service\JWTPayload;
    use Doctrine\ORM\EntityManagerInterface;
    use Exception;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
    use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
    
    /**
     * Class CategoryProductResolver
     * @package App\ArgumentResolver
     */
    class ProductResolver implements ArgumentValueResolverInterface
    {
    
        /** @var EntityManagerInterface  */
        private EntityManagerInterface $em;
        /** @var JWTPayload  */
        private JWTPayload $WTPayload;
    
        /**
         * CategoryProductResolver constructor.
         * @param EntityManagerInterface $em
         */
        public function __construct(EntityManagerInterface $em, JWTPayload $JWTPayload)
        {
            $this->em = $em;
            $this->WTPayload = $JWTPayload;
        }
    
        /**
         * @inheritDoc
         * @throws ArgumentResolverException
         */
        public function supports(Request $request, ArgumentMetadata $argument)
        {
            if (Product::class == $argument->getType()) {
                if (!$request->get('product_id', false))
                {
                    throw new ArgumentResolverException("Один из указанных параметров отсутствует или недействителен.", RestCode::INVALID_PARAMETERS);
                }
                return true;
            }
            return false;
        }
    
        /**
         * @inheritDoc
         * @throws Exception
         */
        public function resolve(Request $request, ArgumentMetadata $argument)
        {
            $product = $this->em->getRepository(Product::class)
                ->getById(
                    $request->get("product_id", 0),
                    $this->WTPayload->has('sale_point_id') ? $this->WTPayload->get('sale_point_id', '/\d+/', 0) : 0
                );
    
            if (!is_a($product, Product::class)) {
                throw new ArgumentResolverException("Продукт не найден.", RestCode::PRODUCT_NOT_FOUND);
            }
    
            yield $product;
        }
    }
  • Symfony, Argument resolver используется по назначению?

    IgorPI
    @IgorPI Автор вопроса
    Немного разобрался в прелестях использования AVR (Argument Value Resolver)

    Максим Федоров,
    Ваша цитата.
    Можно только поабстрактней сделать, чобы любая сущность попадала и проверялась, если указзали, а не для каждой делатьсвой,


    А что если для каждого свой?
    Я за счёт этого хочу реализовать проверку обязательных аргументов.

    Например, рассмотрим следующую ситуацию.
    Есть сущность Product

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

    На случай если не передан обязательный параметр product_id (например)
    Бросать исключение и далее вывод ошибки в слушателе.

    У меня начинает все усложнятся.
    Хотя я этого не хочу.


    Один резольвер для сущности Product
    Будет работать по-разному.

    Например

    Для администратора торговой точки в которой он авторизовался
    Запрос
    GET /products.getById?product_id=1&lang=en HTTP/1.1
    Host: 127.0.0.100:8012
    Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1ODM2MDI3ODAsIm5iZiI6MTU4MzYwMjc4MSwiZXhwIjoxNTgzNjM4NzgwLCJ1c2VyX2lkIjoxLCJzYWxlX3BvaW50X2lkIjoxfQ.NXgD5GJq1bktVnVi38zbsW6xtXvA1dGBoN5YaTfl2-o


    Для поситителя
    Запрос
    GET /products.getById?product_id=1&lang=ru HTTP/1.1
    Host: 127.0.0.100:8011


    Оба они получат
    {
        "code": 0,
        "data": {
            "categories": [
                {
                    "id": 3,
                    "name": "Горячие напитки",
                    "image_url": "http://127.0.0.100:8013/sale_point_1/categories/52b9792086f8f70763c2301ad4c34c8b-5e63547e39edd.png"
                }
            ],
            "images": [
                {
                    "id": 3,
                    "url": "http://127.0.0.100:8013/sale_point_1/products/ed8252ef39def9b5bad534db15fa3ba1-5e63c8b0b0c8d.jpg"
                }
            ],
            "state": false,
            "old_price": "0.00",
            "price": "100.00",
            "short_description": "Чай — напиток, получаемый варкой, завариванием и/или настаиванием листа чайного куста, который предварительно подготавливается специальным образом.",
            "name": "Чай",
            "id": 1,
            "recommendations": [],
            "serial_number": "S1-5E63B30560400",
            "rating": 1.1,
            "long_description": "Чай — напиток, получаемый варкой, завариванием и/или настаиванием листа чайного куста, который предварительно подготавливается специальным образом. Чай — также сам лист чайного куста, обработанный и подготовленный для приготовления напитка. "
        }
    }


    Но в случае если администратор авторизован в другой торговой точке и которой не пренадлежит этот продукт, он получит следующее сообщение

    {
        "code": 2000,
        "msg": "Продукт не найден.",
        "request": {
            "http_method": "GET",
            "request_method": "/products.getById",
            "parameters": []
        }
    }


    Информация идентификатора торговой точки зашита в JWT и естественно подписана ключом
  • Событие для сущности перед её обновлением?

    IgorPI
    @IgorPI Автор вопроса
    Пришлось переработать

    /**
         * @param CategoryProduct $entity
         * @param PreFlushEventArgs $eventArgs
         * @return string
         */
        public function save(CategoryProduct $entity, $eventArgs)
        {
            if ($this->computed === false) {
                if ($this->support()) {
                    foreach ($this->white_mime_list as $mmt => $ext) {
                        if ($mmt == $this->getMimeType()) {
                            $file_name = sprintf("%s-%s.%s", md5($this->getImageContentBase64()), uniqid(), $ext);
                            $relative_path = sprintf("/sale_point_%s/categories/%s", $entity->getSalePoint()->getId(), $file_name);
                            $absolute_path = sprintf("%s%s", $this->images_directory, $relative_path);
    
                            if (!is_dir(dirname($absolute_path))) {
                                mkdir(dirname($absolute_path), 0777, true);
                            }
    
                            $entity->setImage($relative_path);
    
                            $this->computed = true;
                            $eventArgs->getEntityManager()->getUnitOfWork()->computeChangeSets();
    
                            @file_put_contents($absolute_path, base64_decode($this->getImageContentBase64()));
                        }
                    }
                }
            }
        }


    Так как
    $eventArgs->getEntityManager()->getUnitOfWork()->computeChangeSets();

    Снова вызывает preFlush
    Это приводит к зацикливанию.
  • Событие для сущности перед её обновлением?

    IgorPI
    @IgorPI Автор вопроса
    Максим Федоров, Это ответ.

    Сделал что-то типа
    /**
         * @param CategoryProduct $entity
         * @param PreFlushEventArgs $eventArgs
         * @return string
         */
        public function save(CategoryProduct $entity, PreFlushEventArgs $eventArgs)
        {
            if ($this->support()) {
                foreach ($this->white_mime_list as $mmt => $ext) {
                    if ($mmt == $this->getMimeType()) {
                        $file_name = sprintf("%s-%s.%s", md5($this->getImageContentBase64()), uniqid(), $ext);
                        $relative_path = sprintf("/sale_point_%s/categories/%s", $entity->getSalePoint()->getId(), $file_name);
                        $absolute_path = sprintf("%s%s", $this->images_directory, $relative_path);
    
                        if (!is_dir(dirname($absolute_path))) {
                            mkdir(dirname($absolute_path), 0777, true);
                        }
    
                        $entity->setImage($relative_path);
                        $eventArgs->getEntityManager()->getUnitOfWork()->recomputeSingleEntityChangeSet($eventArgs->getEntityManager()->getClassMetadata(CategoryProduct::class), $entity);
                        @file_put_contents($absolute_path, base64_decode($this->getImageContentBase64()));
                    }
                }
            }
        }


    Я передаю изображение следующим образом.

    {
        "category_id": "1",
        "name": "Бургеры",
        "description": "Описание",
        "image": {
            "mime_type": "image/png",
            "content": "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"
        }
    }


    Изображения очень маленькие, поэтому в теле JSON
  • Событие для сущности перед её обновлением?

    IgorPI
    @IgorPI Автор вопроса
    Каюсь, был не прав.
    preUpdate прекрасно работает.

    Ну я додумался ловить preUpdate не изменив сам объект. )))

    Задумка в том, что в preUpdate я изменяю старое изображение на новое.
    Но если я только это делаю, не изменив другие данные в сущности, то естественно не видать мне preUpdate
  • Событие для сущности перед её обновлением?

    IgorPI
    @IgorPI Автор вопроса
    tommy-vercetti,
    prePersist срабатывает перед инсертом новой сущности в базу.

    Да, это самое первое о чем я знал.
    Об этом я писал в вопросе.

    Когда я вставил запись и отработал prePersist все ок, я не меняю данные после flush .
  • Событие для сущности перед её обновлением?

    IgorPI
    @IgorPI Автор вопроса
    Забавно...

    postLoad - The postLoad event occurs for an entity after the entity has been loaded into the current EntityManager from the database or after the refresh operation has been applied to it.


  • Где найти годный парсер адресов?

    IgorPI
    @IgorPI Автор вопроса
    Anton Kravchenko, Да, я в курсе.
    Данных более 5 мл.

    Десятку как минимум нужно отвалить.
    Я игрался с аккаунтами в прошлый раз, быстро банили.
  • Где найти годный парсер адресов?

    IgorPI
    @IgorPI Автор вопроса
    Не очень понятно что вы имели в виду под "разложить по полочкам" и какие именно вам нужны полочки.


    Это значит нормализовать.
    В отдельных таблицах.
    Улицы в streets
    Города в cities
  • Где найти годный парсер адресов?

    IgorPI
    @IgorPI Автор вопроса
    Как вариант, я уже пользовался им.
    Только я адрес доставал по координатам.

    Самое забавное, что у меня есть координаты этих адресов ))
    Но есть вероятность погрешности самого дома.
  • Где найти годный парсер адресов?

    IgorPI
    @IgorPI Автор вопроса
    Возможно придётся допиливать своё решение.
    Тогда было бы неплохо составить универсальный набор базовых регулярок, что бы можно было раскидать строку на составные части.

    Город, улица, дом
  • Как сформировать дайджест?

    IgorPI
    @IgorPI Автор вопроса
    Ну и подпись тела запроса уже идёт в base64, второй раз её кодить вряд ли стоит.


    Да, скорее все, только сейчас дошло.
  • Как правильно организовать передачу прав для конкретной роли юзера с back-end на front-end?

    IgorPI
    @IgorPI
    Было бы не плохо иметь специальный метод, который возвращает эти самые привилегии списком.
    А уже потом, в (мобильном приложении, сайте) на основе полученных данных скрывать те или иные разделы.


    Я как раз об этом.

    Дело в том, что этот список скорее всего будет динамическим.
    А специальные метод всегда возвращает актуальные привилегии.

    Другое дело, что такие "привилегии" легко скомпрометировать.

    Эта тема актуальна и для меня.
    По списку задач, следующая.
  • Как подключиться к mysql, которая запущена в контейнере на docker desktop на mac os?

    IgorPI
    @IgorPI
    Если у вас 1+ Dockerfile, их можно запустить с помощью docker-compose
  • Как подключиться к mysql, которая запущена в контейнере на docker desktop на mac os?

    IgorPI
    @IgorPI
    Привожу пример из своего проекта.

    # Development configuration
    version: "3.7"
    
    services:
      ...
    
      # Database
      db:
        image: percona:latest
        container_name: rapp.db
        restart: on-failure
        ports:
          - 127.0.0.100:3306:3306
        expose:
          - 3306
        networks:
          - api
        volumes:
          - ./docker/mysql/dumps:/var/mysql/dumps
          - ./docker/mysql/test-data:/var/mysql/test-data
        env_file:
          - ./docker/mysql/.env
          - ./docker/.env
    
       ...


    Ссылки по теме
    expose
    ports