Добавление служб в контроллер через «container.service_subscriber» не работает должным образом - PullRequest
0 голосов
/ 05 февраля 2020

Я пытаюсь использовать тег 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, он всегда автоматически устанавливает полный контейнер и вместе с этим соответствующее сообщение об устаревании (поскольку я не устанавливаю его сам):

Пользователь не поддерживает: Авто-инъекция контейнера для «WM \ ApiBundle \ Controller \ UserController» устарела с Symfony 4.2. Вместо этого настройте его как службу.

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

Ответы [ 2 ]

2 голосов
/ 05 февраля 2020

Основная проблема c заключается в том, что встроенная функциональность подписчика службы только внедрит указатель службы в конструктор. Обычный контроллер, который расширяет AbstractController, использует autoconfigure, чтобы переопределить это, и использует 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']]]

0 голосов
/ 06 февраля 2020

Решение проблемы состоит в том, чтобы расширить определение службы контроллера с помощью вызова 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 в качестве контейнера содержащий только зарегистрированные сервисы вместо полного контейнера без использования опции autowire.
Sidenote: установка @service_container приведет к внедрению полного контейнера.

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

...