Все сервисы Хабра

Сообщество IT-специалистов

Ответы на любые вопросы об IT

Профессиональное развитие в IT

Удаленная работа для IT-специалистов

Войти на сайт
  • Все вопросы
  • Все теги
  • Пользователи

Хабр Q&A — вопросы и ответы для IT-специалистов

Получайте ответы на вопросы по любой теме из области IT от специалистов в этой теме.

Узнать больше
другие проекты хабра
  • Хабр
  • Карьера
  • Фриланс
Задать вопрос
voronkovich

voronkovich

  • 116
    вклад
  • 1
    вопрос
  • 104
    ответа
  • 37%
    решений
Ответы
  • Информация
  • Ответы
  • Вопросы
  • Комментарии
  • Подписки
  • Нравится
  • Достижения
  • Можно ли в Symfony инициализировать в конструкторе сервисы если их не используешь?

    voronkovich
    voronkovich @voronkovich
    Под ваше описание подходит Service Subscriber

    Что такое "подписчик сервисов"?

    Это сервис, который получает свои зависимости в виде локатора (https://en.wikipedia.org/wiki/Service_locator_pattern). При этом, локатор реализует ленивую загрузку, что позволяет не создавать экземпляры сервисов без необходимости.

    Когда его следует использовать?

    Когда у сервиса много зависимостей, но не все они используются им одновременно.
    Примером, может служить AbstractController, который зависит от Twig, Doctrine, Router и т.д.
    Очевидно, что не для каждого запроса нужен Twig (json api), и не в каждом запросе есть обращение к БД.
    См. https://github.com/symfony/symfony/blob/e1581a0937...

    Как его создать?

    Реализовать интерфейс ServiceSubscriber:

    <?php
    
    use Psr\Container\ContainerInterface;
    use Symfony\Contracts\Service\ServiceSubscriberInterface;
    
    class SomeService 
    {
        public function __constructor(
            private ContainerInterface $container,
        ) {
        }
        
        public static function getSubscribedServices(): array
        {
            return [
                ServiceFirst::class => ServiceFirst::class,
                ServiceSecond::class => ServiceSecond::class,
                SomeCommonService::class => SomeCommonService::class,
            ];
        }
        
        public function methodFirst()
        {
            $this->container[SomeCommonService::class]->doSomethingCommon();
            $this->container[ServiceFirst::class]->doSomethingFirst();
    
            return $this->commonMethod();
        }
        
        public function methodSecond()
        {
            $this->container[SomeCommonService::class]->doSomethingCommon();
            $this->container[ServiceSecond::class]->doSomethingSecond();
            
            return $this->commonMethod();
        }
    
        private function commonMethod(): string
        {
            // some code
            
            return 'some string';
        }
    }


    Однако, я соглашусь с ответами выше. В подавляющем большинстве случаев лучше будет разбить на несколько сервисов.
    Ответ написан 22 авг. 2022
    1 комментарий
    1 комментарий
  • Symfony 4 как правильно делать deploy на продакшн?

    voronkovich
    voronkovich @voronkovich
    Сборку фронтенда лучше делать локально. Я, например, обычно для таких вещей использую Ansible.

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

    #!/bin/sh
    
    # Создание временной директории
    cd "$(mktemp --directory)";
    
    # Клонирование репозитория во временную директорию
    git clone https://github.com/symfony/demo .;
    
    # Извлечение ветки для релиза
    git checkout master;
    
    # Установка зависимостей фронтенда
    npm install
    
    # Сборка фронтенда
    node_modules/.bin/encore production
    
    # Загрузка кода из временной директории на сервер при помощи rsync
    
    # Остальные действия производятся уже на сервере
    Ответ написан более трёх лет назад
    Комментировать
    Комментировать
  • Как правильно добавить сторонний бандл в Symfony 4.1?

    voronkovich
    voronkovich @voronkovich
    Я повторил ваши шаги на чистой установке. Всё работает нормально.

    1. Попробуйте очистить кэш: rm -rf var/cache.

    2. В Symfony есть несколько команд для отладки подобных ошибок:

    $ bin/console debug:config
    
    $ bin/console debug:config gesdinet_jwt_refresh_token


    Посмотрите, что они выведут.
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Как сделать на странице динамическую подгрузку, статического контента?

    voronkovich
    voronkovich @voronkovich
    Если контент статический, вы можете просто вставить его HTML-код в шаблон. В иных случаях лучше использовать HMVC: How to Embed Controllers in a Template

    В демо приложении есть два примера, иллюстрирующих вставку блоков с контентом: https://github.com/symfony/demo/blob/bbe5180a8c3b6... и https://github.com/symfony/demo/blob/bbe5180a8c3b6...

    В последнем примере также используется ESI для кэширования блока.

    Также, если блок статический, то для него можно не создавать отдельный контроллер, а использовать TemplateController: How to Render a Template without a custom Controller
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Где и когда стоит использовать partial объекты doctrine?

    voronkovich
    voronkovich @voronkovich
    На мой взгляд их лучше не использовать вообще.

    1. Эта идея была взята из Hibernate. Но, Java позволяет в последствии сделать дозагрузку данных которые не были загружены изначально. Т.е. использование partial objects не может привести к ошибке. В PHP у объектов нет API аналогичного Java. У меня есть подозрение, что именно поэтому partial objects будут убраны из Doctrine 3, как это стало с поддержой операций detach/merge.

    2. Реализация partial objects в Doctrine противоречит принципу "Модель всегда должна быть валидна"

    Кстати, вы забыли про вариант с использование легковесного DTO (см. паттерн Flyweight), вместо массивов. См. https://www.doctrine-project.org/projects/doctrine...

    Когда можно использовать массивы?

    Массивы легко сериализуются, например, с ними можно быстро сделать API:
    public function api(EntityManagerInterface $entityManager): Response
    {
        $posts = $entityManager->createQuery('SELECT p FROM App\Entity\Post p')->getArrayResult();
    
        return new JsonResponse($posts);
    }
    Ответ написан более трёх лет назад
    5 комментариев
    5 комментариев
  • Как добавить свою функцию для twig?

    voronkovich
    voronkovich @voronkovich
    Вам нужно создать расширение для Twig и реализовать функцию там. См. https://twig.symfony.com/doc/2.x/advanced.html#id2
    Ответ написан более трёх лет назад
    2 комментария
    2 комментария
  • Как работает подход Unit of Work?

    voronkovich
    voronkovich @voronkovich
    Попробую привести очень примитивный пример. Допустим, мы делаем простое приложение для микроблоггинга. Каждая сущность будет иметь вид:

    class Tweet
    {
        private $id;
        private $content;
    
        public function __construct(int $id, string $content)
        {
            $this->id = $id;
            $this->content = $content;
        }
    
        public function getId(): int
        {
            return $this->id;
        }
    
        public function getContent(): string
        {
            return $this->content;
        }
    
        public function setContent(string $content): void
        {
            $this->content = $content;
        }
    }


    Схема данных:

    CREATE TABLE tweets (
        id INTEGER PRIMARY KEY,
        content VARCHAR(255) NOT NULL
    )


    Следующая реализация UnitOfWork будет иметь несколько ограничений:
    1. Она умеет работать только с Tweet;
    2. Она умеет только загружать сущности и сохранять произведённые в них изменения.
    class UnitOfWork
    {
        private $connection;
        private $identityMap;
        private $data;
    
        public function __construct(\PDO $connection)
        {
            $this->connection = $connection;
            $this->identityMap = [];
            $this->data = [];
        }
    
        public function find(int $id): Tweet
        {
            if (isset($this->identityMap[$id])) {
                return $this->identityMap[$id];
            }
    
            $query = $this->connection->prepare('SELECT * FROM tweets WHERE id = ?');
            $query->execute([ $id ]);
    
            if (false === $data = $query->fetch()) {
                throw new \Exception(\sprintf('Tweet with id "%d" not found.', $id));
            }
    
            $id = (int) $data['id'];
    
            // Исходные данные сохраняются для того, чтобы в дальнейшем вычислить изменения.
            $this->data[$id] = $data;
    
            $tweet = new Tweet($id, $data['content']);
    
            $this->identityMap[$id] = $tweet;
    
            return $tweet;
        }
    
        public function commit(): void
        {
            // Вообще говоря, лучше вычислить все изиенения, создать один "большой" запрос
            // и выполнить его внутри транзакции, но для простоты мы сделаем для каждого
            // изменения отдельный запрос
            $query = $this->connection->prepare('UPDATE tweets SET content = ? WHERE id = ?');
            foreach ($this->identityMap as $tweet) {
                if ($tweet->getContent() !== $this->data[$tweet->getId()]['content']) {
                    $query->execute([ $tweet->getContent(), $tweet->getId() ]);
                }
            }
        }
    }


    Полный пример можете скачить и посмотреть тут: https://gist.github.com/voronkovich/d35cdcdf6eb09e...
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Аналог Laravel Service Provider для Symfony 4?

    voronkovich
    voronkovich @voronkovich
    Прямой аналог это - container extension. Подробнее см. https://symfony.com/doc/current/components/depende...
    Код для загрузки расширения следует поместить в src/Kernel.php в метод configureContainer.
    Ответ написан более трёх лет назад
    Комментировать
    Комментировать
  • Как правильнее проверять CSRF токены?

    voronkovich
    voronkovich @voronkovich
    Оба способа правильные. Просто второй более устойчив к CSRF-атакам. Например, компонет форм по умолчанию использует имя класса формы для поля "intention" ("ключевое слово", если использовать ваше определение): https://github.com/symfony/form/blob/a0386553fabb5...

    С другой стороны, есть мнение, что использование различных токенов, не делает приложение более безопасным. См. https://github.com/symfony/symfony/issues/18115

    Лично я использую первый вариант т.е. один токен на все приложение.
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Как в symfony 4 разбить большую форму на вкладки?

    voronkovich
    voronkovich @voronkovich
    Нужно просто вручную вывести поля формы в коде вкладок в шаблоне (How to Control the Rendering of a Form). Если это админка, и вы используете bootstrap, можете использовать табы: https://getbootstrap.com/docs/4.0/components/navs/#tabs.
    Основная проблема при использовании вкладок заключается в том, что вам нужно, чтобы после отправки формы текущая вкладка осталась открытой. Поэтому, нужно сохранять идентификатор текущей вкладки в хранилище браузера. Пример можете найти здесь: How to keep the current tab active on page reload ....

    Примерно так это может выглядеть:

    {% extends 'base.html.twig' %}
    
    {% block body %}
    <ul class="nav nav-tabs" role="tablist">
        <li class="nav-item">
            <a class="nav-link active" href="#general-tab" data-toggle="tab" role="tab" aria-selected="true">
                General
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#security-tab" data-toggle="tab" role="tab" aria-selected="false">
                Security
            </a>
        </li>
    </ul>
    <div class="tab-content">
        {{ form_start(form) }}
        {{ form_errors(form) }}
        <div class="tab-pane active show" id="general-tab" role="tabpanel" aria-labelledby="general-tab">
            {{ form_row(form.field1) }}
            {{ form_row(form.field2) }}
        </div>
        <div class="tab-pane" id="security-tab" role="tabpanel" aria-labelledby="security-tab">
            {{ form_row(form.field3) }}
            {{ form_row(form.field4) }}
        </div>
        <button type="submit">Submit</button>
        {{ form_end(form) }}
    </div>
    {% endblock %}
    
    {% block javascripts %}
    {{ parent() }}
    <script charset="utf-8">
    $(function() {
        $('a[data-toggle="tab"]').on('show.bs.tab', function(e) {
            localStorage.setItem('activeTab', $(e.target).attr('href'));
        });
    
        var activeTab = localStorage.getItem('activeTab');
    
        if (activeTab) {
            $('.nav-tabs a[href="' + activeTab + '"]').tab('show');
        }
    });
    </script>
    {% endblock javascripts %}
    
    {% block stylesheets %}
    {{ parent() }}
    <style type="text/css" media="screen">
    .tab-pane:not(.show) {
        display: none;
    }
    .tab-pane.show {
        display: block;
    }
    </style>
    {% endblock  %}
    Ответ написан более трёх лет назад
    Комментировать
    Комментировать
  • Doctrine почему не рабоатет dql, что я делаю не так?

    voronkovich
    voronkovich @voronkovich
    Вы запрос неправильно составляете. Должно быть примерно так (полагаю, у вас сконфигурирована связь @ManyToOne между Employee и Department):

    SELECT MAX(employee.salary)
    FROM AppBundle:Employee employee
    JOIN employee.department department
    WHERE department.id = :id


    Можно сделать без доп. соединения, если использовать DQL-функцию IDENTITY:

    SELECT MAX(employee.salary)
    FROM AppBundle:Employee employee
    WHERE IDENTITY(employee.department) = :id
    Ответ написан более трёх лет назад
    Комментировать
    Комментировать
  • Phpunit в Symfony 4, изменить URL сайта?

    voronkovich
    voronkovich @voronkovich
    Попробуйте так:

    $client = self::createClient();
    
    $client->setServerParameter('HTTP_HOST', 'symfony-test');
    Ответ написан более трёх лет назад
    3 комментария
    3 комментария
  • Задавать явно все default значения для полей через php каждый раз при создании объекта?

    voronkovich
    voronkovich @voronkovich
    Я бы использовал оба способа одновременно:
    • Указывать значение по умолчанию в сущности необходимо, т.к. сущность всегда должна быть валидна;
    • Указывать значение по умолчанию для столбца тоже необходимо, т.к. бывают случаи когда приходится работать с "голым" SQL.
    <?php
    
    namespace App\Entity;
    
    /**
     * @Entity
     */
    class User
    {
        const DEFAULT_ROLE = 'user';
    
        /**
         * @Column(type="string", options={"default": User::DEFAULT_ROLE})
         *
         * @var string
         */
        private $role = self::DEFAULT_ROLE;
    }
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Symfony4 Flex + Doctrine: какой бандл для data grid можно прикрутить?

    voronkovich
    voronkovich @voronkovich
    Перепробывал несколько бандлов - все ругаются на зависимости от симфы.


    Я только что попробовал самый первый из списка: sg/datatablesbundle. Установился нормально. К нему даже рецепт есть.
    Ответ написан более трёх лет назад
    3 комментария
    3 комментария
  • Как получить уникальные значение полей в Symfony EntityType?

    voronkovich
    voronkovich @voronkovich
    Вообще говоря, вам лучше сделать нормализацию таблицы локаций и вынести города в отдельную сущность City.

    Оператор DISTINCT применяется ко всему набору данных, и в вашем случае не сработал бы. Да и его аналога в QueryBuilder нет. См. https://www.doctrine-project.org/api/dbal/2.6/Doct...

    Правильно будет так, как выше советует Денис Дерепко, т.е. использовать QueryBuilder#groupBy:

    $repository
        ->createQueryBuilder('l')
        ->orderBy('l.addressCity', 'ASC')
        ->groupBy('l.addressCity');
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
  • Symfony 4 REST API: как правильно включить метод OPTIONS?

    voronkovich
    voronkovich @voronkovich
    В простейшем случае, вы можете вынести обработку OPTIONS в отдельный метод:

    <?php
    
    namespace App\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    
    /**
     * @Route("/blog")
     */
    class BlogController extends AbstractController
    {
        /**
         * @Route("/", methods="OPTIONS")
         */
        public function options(Request $request): Response
        {
            $response = new Response();
    
            $response->headers->set('Access-Control-Allow-Methods', 'OPTIONS, GET');
    
            return $response;
        }
    
        /**
         * @Route("/", methods="GET", name="blog_index")
         */
        public function index(Request $request): Response
        {
            return $this->json([
                'message' => 'hello',
            ]);
        }
    }
    Ответ написан более трёх лет назад
    7 комментариев
    7 комментариев
  • Как создавать Bundles в Symfony 4?

    voronkovich
    voronkovich @voronkovich
    1. Создаете директорию для бандлов (например, bundles как предлагает Максим Федоров)

    2. Создаете директорию бандла. Например, bundles/AcmeBundle

    3. В composer.json добавляете:

    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "AcmeBundle\\": "bundles/AcmeBundle/"
        },
    }


    4. В директории бандла создаете его класс:

    <?php
    
    namespace AcmeBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    
    class AcmeBundle extends Bundle
    {
    }


    5. Регистрируете бандл в файле config/bundles.php:

    return [
        Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
        AcmeBundle\AcmeBundle::class => ['all' => true],
    ];
    Ответ написан более трёх лет назад
    Комментировать
    Комментировать
  • Symfony 4 - Как сделать редирект в Сервисе?

    voronkovich
    voronkovich @voronkovich
    Сделайте подписчик события 'kernel.request'. См. https://symfony.com/doc/current/components/http_ke...

    Пример можете в демо-приложении посмотреть: https://github.com/symfony/demo/blob/master/src/Ev...
    Ответ написан более трёх лет назад
    2 комментария
    2 комментария
  • Symfony 4 - Doctrine - Repository как вытащить правельный тип данных??

    voronkovich
    voronkovich @voronkovich
    Никак. Доктрина не делает приведение типов данных при выполнении обычного SQL-запроса. Используйте array_map.
    Ответ написан более трёх лет назад
    2 комментария
    2 комментария
  • Symfony Routing Component v4 как импортировать второй yaml-файл приложения (правильно указать путь)?

    voronkovich
    voronkovich @voronkovich
    Параметры не сработают. Контейнер и роутер ничего не знают друг о друге. В Symfony вся магия сделана в FrameworkBundle: https://github.com/symfony/framework-bundle/blob/8...

    Symfony 4 - это микрофреймворк, лучше используйте его, а не собирайте собственный:

    $ composer create-project symfony/skeleton

    Также, вы можете передать в FileLocator несколько путей:

    $fileLocator = new FileLocator([ __DIR__, __DIR__.'/../../app' ]);


    См. https://api.symfony.com/4.0/Symfony/Component/Conf...
    Ответ написан более трёх лет назад
    1 комментарий
    1 комментарий
Оценили как «Нравится»
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • Следующие →
Самые активные сегодня
  • Evgenii
    • 11 ответов
    • 0 вопросов
  • Enokin
    Владислав
    • 8 ответов
    • 0 вопросов
  • mayton2019
    • 7 ответов
    • 0 вопросов
  • Drno
    • 7 ответов
    • 0 вопросов
  • Rsa97
    Rsa97
    • 5 ответов
    • 0 вопросов
  • sharp97
    sharp97
    • 4 ответа
    • 1 вопрос
  • © Habr
  • О сервисе
  • Правила
  • Обратная связь
  • Блог

Войдите на сайт

Чтобы задать вопрос и получить на него квалифицированный ответ.
Войти через центр авторизации