Arhn - архитектура программирования

Добавление сервисов в контроллер через container.service_subscriber не работает должным образом

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

Структура контроллера следующая:

У меня есть база BaseController, которая расширяется от AbstractFOSRestController FOSRestBundle, который имеет некоторые общие используемые методы для всех моих контроллеров. Этот сервис будет использоваться как parent для других моих контроллеров.

Определение сервиса выглядит так:

WM\ApiBundle\Controller\BaseController:
    class: WM\ApiBundle\Controller\BaseController
    abstract: true
    arguments:
        - "@service1"
        - "@service2"
        - ...

WM\ApiBundle\Controller\UserController:
    parent: WM\ApiBundle\Controller\BaseController
    public: true
    #autowire: true
    class: WM\ApiBundle\Controller\UserController
    tags:
        - { name: 'container.service_subscriber'}
        - { name: 'container.service_subscriber', key: 'servicexyz', id: 'servicexyz' }

Класс выглядит так:

/**
 * User controller.
 */
class UserController extends AbstractCRUDController implements ClassResourceInterface
{

    public static function getSubscribedServices()
    {
        return array_merge(parent::getSubscribedServices(), [
        'servicexyz' => ServiceXYZ::class,
        ]);
    }
   .......
}

У меня проблема в том, что если я устанавливаю autowire: false, он всегда автоматически устанавливает полный контейнер и с этим соответствующее сообщение об устаревании (поскольку я сам его не устанавливаю):

User Deprecated: автоматическое внедрение контейнера для «WM\ApiBundle\Controller\UserController» устарело, начиная с Symfony 4.2. Вместо этого настройте его как службу.

При установке autowire: true Symfony учитывает тег container.service_subscriber и устанавливает только частичный контейнер (ServiceLocator), что также устранит сообщение об устаревании. Я ожидал, что автосвязывание не должно иметь никакого значения в этом случае, потому что я явно указываю сервису, какие другие сервисы у него должны быть.
Я неправильно использую теги или у меня есть общая проблема с пониманием того, как подписаться на обслуживание контроллера?


Ответы:


1

Основная проблема заключается в том, что встроенная функциональность подписчика службы будет внедрять только локатор службы в конструктор. Обычный контроллер, который расширяет AbstractController, использует автоконфигурацию, чтобы в основном переопределить это, и использует setContainer вместо конструктора.

# ApiBundle/Resources/config/services.yaml
services:
  _defaults:
    autowire: false
    autoconfigure: false

  Api\Controller\UserController:
    public: true
    tags: ['container.service_subscriber']
class UserController extends AbstractController
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }
    public static function getSubscribedServices()
    {
        return array_merge(parent::getSubscribedServices(), [
            // ...
            'logger' => LoggerInterface::class,
        ]);
    }
    public function index()
    {
        $url = $this->generateUrl('user'); // Works as expected

        // $signer = $this->get('uri_signer'); // Fails as expected

        $logger = $this->get('logger'); // Works as expected

        return new Response('API Index Controller ' . get_class($this->container));
    }
}

Результат:

    API Index Controller Symfony\Component\DependencyInjection\Argument\ServiceLocator

Указывает, что вводится локатор службы (в отличие от глобального контейнера).

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

  Api\Controller\UserController:
    public: true
    tags: ['container.service_subscriber']
    calls: [['setContainer', ['@Psr\Container\ContainerInterface']]]

05.02.2020
  • Спасибо за ваше время и ответ. Но разве это не противоречит документам? И здесь на самом деле даже говорится, если Я не хочу использовать автоконфигурацию, я должен просто использовать тег. Также протестировано только с включенной автонастройкой, которая не изменила поведение, единственный способ сделать это, как ожидалось (или, по крайней мере, как я понимаю), - это включить автосвязь. 06.02.2020
  • Нашел решение моей проблемы. На самом деле уже была проблема в проекте symfony. Чего мне не хватало, так это вызова ['setContainer', ['@Psr\Container\ContainerInterface']], который дал бы мне ServiceLocator. Однажды я попробовал это с service_container, но это всегда будет содержать полный контейнер, так что, вероятно, это меня смутило. 06.02.2020
  • Прохладный. Я пытался понять, как использовать вызовы, но никогда не пробовал использовать Psr ContainerInterface. Я добавил это как вариант ответа. 06.02.2020

  • 2

    Решение проблемы заключается в расширении определения службы контроллера вызовом setContainer для внедрения службы '@Psr\Container\ContainerInterface':

    WM\ApiBundle\Controller\BaseController:
        class: WM\ApiBundle\Controller\BaseController
        abstract: true
        arguments:
            - "@service1"
            - "@service2"
            - ...
        calls: 
            - ['setContainer', ['@Psr\Container\ContainerInterface']]
    
    WM\ApiBundle\Controller\UserController:
        parent: WM\ApiBundle\Controller\BaseController
        public: true
        class: WM\ApiBundle\Controller\UserController
        tags:
            - { name: 'container.service_subscriber'}
            - { name: 'container.service_subscriber', key: 'servicexyz', id: 'servicexyz' }
    

    Это даст мне контейнер ServiceLocator as, содержащий только зарегистрированные сервисы, а не полный контейнер без использования параметра autowire.
    Примечание: установка @service_container приведет к внедрению полного контейнера.

    Для полноты картины уже существовала проблема в проекте symfony, где это обсуждалось.

    06.02.2020
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..