Задать вопрос
  • Как образом можно вывести такую структуру?

    BoShurik
    @BoShurik
    Symfony developer
    <?php
    $items = range(1, 30);
    $chunks = array_chunk($items, 5);
    $chunks = array_map(function($items){
        return array_chunk($items, 4);
    }, $chunks);
    ?>
    
    <?php foreach ($chunks as $chunk): ?>
        <div class="item">
            <div class="item-left">
                <?php foreach ($chunk[0] as $item): ?>
                    <div class="point"><?=$item?></div>
                <?php endforeach; ?>
            </div>
            <div class="item-right">
                <?php foreach ($chunk[1] as $item): ?>
                    <div class="point"><?=$item?></div>
                <?php endforeach; ?>
            </div>
        </div>
    <?php endforeach; ?>


    Если элементов не всегда 30, то надо делать проверку на существование $chunk[1]
    Ответ написан
    Комментировать
  • Проблема в каждом втором запросе?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Вам нужно либо реализовать примитивный провайдер, который будет возвращать new User() вместо вашего кода
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return new User();
    }

    либо добавить stateless: true
    Проблема в ContextListener который
    ContextListener manages the SecurityContext persistence through a session.

    Он ожидает, что в сессии находится токен пользователя и пытается его восстановить из провайдера, которого у вас нет.
    Ответ написан
    2 комментария
  • Как работает ЭЦП на сайте?

    BoShurik
    @BoShurik
    Symfony developer
    По поводу аутентификации вот статья на хабре: https://habr.com/ru/post/123372/, только в вашем случае не надо самому генерировать ключи

    Соответственно вам придется поставить КриптоПро CSP для проверки подписи на сервере. Для подписи сообщений в чате и документов хватит плагина и КриптоПро на клиенте.

    Примеры работы с плагином есть на сайте: https://cpdn.cryptopro.ru/default.asp?url=content/... на их форуме можно задавать вопросы, они оперативно отвечают
    Ответ написан
    Комментировать
  • Symfony serializer как десериализовать массив с объектами?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    $reflectionExtractor = new ReflectionExtractor();
    $phpDocExtractor = new PhpDocExtractor();
    $propertyTypeExtractor = new PropertyInfoExtractor([$reflectionExtractor], [$phpDocExtractor, $reflectionExtractor], [$phpDocExtractor], [$reflectionExtractor], [$reflectionExtractor]);
    
    $normalizer = new ObjectNormalizer(null, null, null, $propertyTypeExtractor);
    $arrayNormalizer = new ArrayDenormalizer();
    $serializer = new Serializer([$arrayNormalizer, $normalizer]);
    return $serializer->denormalize($array, ContragentDto::class);
    Ответ написан
    24 комментария
  • Symfony guard entry_point?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    https://symfony.com/doc/current/security/multiple_...
    firewalls:
        main:
            guard:
                authenticators: 
                    - App\Security\TokenAuthenticator

    entry_point - используется для начала процесса аутентификации. В вашем случае он не нужен, т.к. у вас api
    Ответ написан
  • Как в symfony создать сервис, у которого зависимость определяется окружением?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Если зависимость от APP_ENV, то проще так
    # config/services.yaml
    services:
        DependencyInterface: '@RealDependency'

    # config/services_test.yaml
    services:
        DependencyInterface: '@FakeDependency'

    Если нужна зависимость от какой-то другой переменной окружения, то надо использовать вариант Максим Федоров , т.к. контейнер компилируемый и на этапе компиляция значение из переменных окружения неизвестны
    Ответ написан
    2 комментария
  • Что написать на ооп?

    BoShurik
    @BoShurik
    Symfony developer
    Ответ написан
    Комментировать
  • Как создать рандомную выборку?

    BoShurik
    @BoShurik
    Symfony developer
    Вариант без хранения последовательностей:
    $hosts = [
        'ремонтмитино.рф',
        'ремонт-королев.рф',
        'ремонтчехов.рф',
        'ремонтподольск.рф',
        'ремонтдомодедово.рф',
    ];
    
    $brands = [
        'aeg',
        'samsung',
        'lg',
    ];
    
    $phrases = [
        'Работа холодильника основана на принципе холодильной машины, переносящей тепловую энергию из рабочей камеры – наружу, в кухонное помещение',
        'Холодильники делятся на два вида: среднетемпературные камеры для хранения свежих продуктов и низкотемпературные морозильные камеры',
        'Прототип морозильника появились несколько тысяч лет назад – использовались помещения для хранения продуктов, наполненные льдом',
        'Российский прототип холодильника — ледники, представляющие сруб, погруженный в землю',
        'С 1930 года хладагентом в морозильниках является инертный газ фреон',
        'Хладагент, обычно, фреон — газ, переносящий лишнее для продуктов тепло от испарителя к конденсатору',
        'Холодильник тратит около 10% от общего электропотребления дома',
        'Наиболее экономный режим холодильника - когда в нём находится большое количество продуктов',
    ];
    
    $count = 5;
    foreach ($hosts as $host) {
        foreach ($brands as $brand) {
            srand(crc32($host.$brand)); // Чтобы не хранить результат
    
            $brandPhrases = $phrases;
            shuffle($brandPhrases);
    
            echo sprintf("%s %s\n", $host, $brand);
            $brandPhrases = array_slice($brandPhrases, 0, $count);
            var_dump($brandPhrases);
        }
    }
    Ответ написан
    Комментировать
  • Как сложить элементы массива в разных вариациях?

    BoShurik
    @BoShurik
    Symfony developer
    Вариант с рекурсией (все-таки отмеченное решение уже на 50 элементах заметно тормозит)
    $array = array_fill(0, 50, 1);
    $sum = 20;
    
    function elements($array, $sum, &$result = [], $iteration = [])
    {
        // Если нужны все перестановки, то нужно закомментировать условие
        if (!empty($result)) {
            return;
        }
        if ($sum === 0) {
            $result[] = $iteration;
    
            return;
        }
    
        foreach ($array as $index => $value) {
            if ($sum - $value < 0) {
                continue;
            }
    
            $next = $array;
            unset($next[$index]);
    
            elements($next, $sum - $value, $result, array_merge($iteration, [$index]));
        }
    
        return;
    }
    
    elements($array, $sum, $result);
    
    // Если вытащены все возможные перестановки, то разкомментировать
    // Отсортируем, чтоб убрать повторяющиеся элементы
    //foreach ($result as &$item) {
    //    sort($item);
    //}
    //unset($item);
    //$result = array_unique($result, SORT_REGULAR);
    
    foreach ($result as $item) {
        echo 'Индексы элементов массива, составляющих сумму: ' . implode(' ', $item) . \PHP_EOL;
    }
    Ответ написан
    Комментировать
  • Symfony 4: как отключить кеширование классов и всего такого в var/cache?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Отключать не стоит, т.к. контейнер и роутинг компилируемые by design, т.о. время ответа будет очень долгим (4000ms vs 250ms на моем приложении)
    Если очень хочется, то можно сделать так:
    // \App\Kernel
    protected function initializeContainer()
    {
        if ($_ENV['APP_ENV'] === 'dev') {
            $container = $this->buildContainer();
            $container->setParameter('container.build_id', 'id');
            $container->setParameter('container.build_hash', 'hash');
            $container->set('kernel', $this);
            $container->compile(true);
    
            $this->container = $container;
        } else {
            parent::initializeContainer();
        }
    }

    Но чтоб это заработало, мне пришлось отключить smart-core/accelerator-cache-bundle, возможно другие бандлы тоже несовместимы с таким режимом.

    Так что в вашем случае я бы все-таки рекомендовал как-то законфигурировать докер.
    Ответ написан
    3 комментария
  • Почему in_array в цикле видит только первый элемент?

    BoShurik
    @BoShurik
    Symfony developer
    Проблема в том, что строка из которой создавался массив в конце содержит (скорее всего) перевод строки, т.о. второй (он же последний элемент) содержит непечатаемые символы, которые мешают сравнению.
    $time = array_map('trim', $time);
    решит проблему
    Ответ написан
    Комментировать
  • Как в symfony 3.4 вывести логин (или какие-либо данные) в базовый шаблон?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    1. Рендер контроллера внутри шаблона
    https://symfony.com/doc/3.4/templating/embedding_c...
    {{ render(controller(
        AppBundle:Article:recentArticles',
        { 'max': 3 }
    )) }}

    2. Кастомный Twig Extension, который добавляет функцию, реализующую нужную логику
    https://symfony.com/doc/3.4/templating/twig_extens...
    {{ articles(3) }}
    3. Если надо выводить что-то простое, к примеру, версию приложения, то можно использовать глобальные переменные
    https://symfony.com/doc/3.4/templating/global_vari...
    {{ app_version }}
    4. Какие-то вещи доступны в глобальной переменнойapp, к примеру, тот же инстанс залогиненого пользователя
    {{ app.user.username }}

    Это справедливо для всех версий Symfony
    Ответ написан
    Комментировать
  • Как в symfony 3.4 вывести и отправить форму для сущности в списке сущностей?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    $form = $this->container->get('form.factory')
        ->createNamed(
            'account_'. $account->getId(), 
            AccountType::class, 
            $account
        )
    ;


    По поводу кода
    public function indexAction(Request $request)
    {
        $accounts = $this
            ->getDoctrine()
            ->getRepository('AppBundle:Account')
            ->findActive();
    
        $forms = [];
        foreach ($accounts as $key => $account){
    
            $form = $this->container->get('form.factory')->createNamed('account_'. $account->getId(), AccountType::class, $account);
    
            // Не надо добавлять кнопки сабмита, их рекомендуется добавлять непосредственно в шаблоне обычным html
            // $form->add('submit', SubmitType::class);
    
            $form->handleRequest($request);
    
            // Не надо инжектить форму в сущность, легче просто передать формы в шаблон
            // $account->setForm($form);
            // $account->setFormView($formView);
    
            if($form->isSubmitted() && $form->isValid()){
    
                // Объекты передаются по ссылке, нет смысла в получение $account повторно
                // $data = $form->getData();
    
                $this->addFlash('success', 'Saved');
    
                $em = $this->getDoctrine()->getManager();
                // Это актуально только для создания сущности, при редактировании - это не нужно
                // $em->persist($data);
                $em->flush();
    
                return $this->redirectToRoute('account_list');
            } else {
                $forms[$account->getId()] = $form->createView();
            }
        }
    
        return [
            'forms' => $forms,
            'accounts' => $accounts,
        ];
    }


    {# {{ form_row(account.formView) }} #}
    {{ form_start(forms[account.id]) }}
        {{ form_widget(forms[account.id]) }}
        <button>Submit</button>
    {{ form_end(forms[account.id]) }}


    P.S. В идеале бы разделить обновление и список сущностей по разным экшенам
    Ответ написан
    2 комментария
  • Как правильно использовать роут symfony component?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    index:
      path: /
      defaults:
        _controller: 'App\Controllers\CommentController::get'
      methods: ['GET']
    
    set:
      path: /
      defaults:
        _controller: 'App\Controllers\CommentController::set'
      methods: ['POST']
    Ответ написан
    6 комментариев
  • Как правильно подключить древнюю библиотеку в современный фреймворк (PHP)?

    BoShurik
    @BoShurik
    Symfony developer
    https://getcomposer.org/doc/04-schema.md#classmap
    Положить файлики библиотеки в отдельную директорию (e.g. legacy-lib/) и прописать
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        },
        "classmap": ["legacy-lib/"]
    },
    Ответ написан
    Комментировать
  • Как использовать RabbitMqBundle в Symfony4?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Документация
    class TaskController
    {
        /**
         * @var ProducerInterface
         */
        private $producer;
    
        public function __construct(ProducerInterface $producer)
        {
            $this->producer = $producer;
        }
    
        public function indexAction($name)
        {
            $this->producer->publish('test');
    
            return new Response();
        }
    }

    # services.yaml
    services:
        App\Controller\TaskController:
            arguments:
                - '@old_sound_rabbit_mq.task_producer'

    либо (если producer только одни)
    # services.yaml
    services:
        OldSound\RabbitMqBundle\RabbitMq\ProducerInterface: '@old_sound_rabbit_mq.task_producer'

    и понадеяться на autowire
    Еще может пригодится: Local service binding
    Ответ написан
    8 комментариев
  • Symfony. Вывод списка статей с захватом имени автора статьи с помощью один-ко-многим. Как не захватывать весь объект Автора, а только его имя?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    https://www.doctrine-project.org/projects/doctrine...

    $query = $em->createQuery(
    'SELECT NEW ArticleDTO(ar.title, ar.imagePath, ar.publishedAt, CONCAT(au.firstName, ' ', au.lastName)) 
    FROM Article ar JOIN ar.author au');
    $articles = $query->getResult(); // array of ArticleDTO


    Это можно разместить где-нибудь в репозитории в отдельном методе

    Так же может быть интересен Partial Object, но тут рискуете забыть про то, что объект загружен не полностью и получить кучу сложно отлавливаемых багов
    Ответ написан
    3 комментария
  • Как создать RouteCollection из папки контроллеров с аннотироваными методами?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    1. Вам надо создать AnnotationClassLoader, задачей которого преобразовывать аннотации в роуты. Абстрактный класс есть в компоненте, вам надо только определить метод configureRoute. За основу можно взять код из фреймворка
    Должно получится что-то вроде этого:

    namespace App\Router;
    
    use Symfony\Component\Routing\Loader\AnnotationClassLoader as BaseLoader;
    use Symfony\Component\Routing\Route;
    
    class AnnotationClassLoader extends BaseLoader
    {
        protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot)
        {
            if ('__invoke' === $method->getName()) {
                $route->setDefault('_controller', $class->getName());
            } else {
                $route->setDefault('_controller', $class->getName().'::'.$method->getName());
            }
        }
    }



    2. И сконфигурировать все это дело (придется использовать symfony/config):
    use секция, на всякий случай

    use App\Router\AnnotationClassLoader;
    use Doctrine\Common\Annotations\AnnotationReader;
    use Doctrine\Common\Annotations\AnnotationRegistry;
    use Symfony\Component\Config\FileLocator;
    use Symfony\Component\Config\Loader\DelegatingLoader;
    use Symfony\Component\Config\Loader\LoaderResolver;
    use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
    use Symfony\Component\Routing\Loader\AnnotationFileLoader;
    use Symfony\Component\Routing\Router;


    $autoloader = require_once __DIR__ . '/../vendor/autoload.php';
    
    AnnotationRegistry::registerLoader(array($autoloader, 'loadClass'));
    
    $reader = new AnnotationReader();
    $annotationClassLoader = new AnnotationClassLoader($reader);
    
    $fileLocator = new FileLocator([__DIR__]);
    
    $loaderResolver = new LoaderResolver([
        new AnnotationFileLoader($fileLocator, $annotationClassLoader),
        new AnnotationDirectoryLoader($fileLocator, $annotationClassLoader),
    ]);
    
    $loader = new DelegatingLoader($loaderResolver);
    
    $router = new Router($loader, __DIR__ . '/../src/Controller');
    
    dump($router->getRouteCollection());
    Ответ написан
    2 комментария
  • Symfony Form & FOSRESTBundle: Как загрузить файл?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Вы забыли добавить данные из files. Можно посмотреть как это сделано в HttpFoundationRequestHandler
    $params = $request->request->all();
    $files = $request->files->all();
    
    $data = array_replace_recursive($params, $files);
    
    $form->submit($data);


    Если вы хотите посылать json + файл, то файл придется передавать в base64: https://stackoverflow.com/questions/4083702/postin...
    либо немного подкорректировать структуру запроса (json передавать в одном поле, а файлы - в другом):
    https://stackoverflow.com/a/13076550/1247234

    В первом случае использование формы под вопросом (придется какой-то кастомный DataTransformer изобретать), а вот во втором - вполне себе можно пробросить соответствующие данные в submit как показано выше

    Но все-таки лучше формы не использовать, а делать напрямую через serializer/validator: Как правильно фильтровать и мапить данные при реализации API на Symfony4? , т.к. основная фишка форм - это их вывод, который вы не используете
    Ответ написан
    1 комментарий