Symfony 3: невозможно получить доступ к контейнеру изнутри контроллера

Я переношу свое приложение с Symfony 2.8 на Symfony 3.3.

Изнутри моего контроллера у меня есть это:

public function indexAction()
{
    $email = new Email();

    $form = $this->createForm(GetStartedType::class, $email, [
        'action' => $this->generateUrl('get_started_end'),
        'method' => 'POST',
    ]);

    return [
        'form' => $form->createView(),
    ];
}

Но я получаю это исключение:

Вызов функции-члена get() при нулевом значении

Мой контроллер расширяет Symfony\Bundle\FrameworkBundle\Controller\Controller:

/**
 * {@inheritdoc}
 */
class DefaultController extends Controller
{
...
}

Так что у меня есть доступ к контейнеру.

Делая дампы в коде Symfony, я вижу, что контейнер установлен правильно:

namespace Symfony\Component\DependencyInjection;

/**
 * ContainerAware trait.
 *
 * @author Fabien Potencier <[email protected]>
 */
trait ContainerAwareTrait
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * Sets the container.
     *
     * @param ContainerInterface|null $container A ContainerInterface instance or null
     */
    public function setContainer(ContainerInterface $container = null)
    {
        dump('Here in the ContainerAwareTrait');
        dump(null === $container);
        $this->container = $container;
    }
}

Это свалки

Here in the ContainerAwareTrait
false

Так что автовайринг работает хорошо и устанавливает контейнер.

А вот в ControllerTrait у меня так:

trait ControllerTrait
{
    /**
     * Generates a URL from the given parameters.
     *
     * @param string $route         The name of the route
     * @param mixed  $parameters    An array of parameters
     * @param int    $referenceType The type of reference (one of the constants in UrlGeneratorInterface)
     *
     * @return string The generated URL
     *
     * @see UrlGeneratorInterface
     */
    protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH)
    {
        dump('Here in the ControllerTrait');
        die(dump(null === $this->container));
        return $this->container->get('router')->generate($route, $parameters, $referenceType);
    }

    ...

это дамп:

Here in the ControllerTrait
true

Итак, здесь container равно null, и это вызывает ошибку.

Кто-нибудь может помочь мне решить эту проблему?

Почему container имеет значение null?

Если это может помочь, это конфигурация services.yml (по умолчанию поставляется с Symfony):

# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
    resource: '../../src/AppBundle/Controller'
    public: true
    tags: ['controller.service_arguments']

Этот вопрос опубликован как проблема в системе отслеживания проблем Symfony.


person Aerendir    schedule 15.06.2017    source источник
comment
У меня не было возможности поэкспериментировать с новым контроллером autowire. Ради интереса попробуйте добавить вызовы: [[setContainer, ['@service_container']]] в services.yml. Не должно быть необходимым.   -  person Cerad    schedule 15.06.2017
comment
Возможный дубликат: Symfony Controller не может получить доступ к контейнеру?   -  person ccKep    schedule 15.06.2017
comment
Возможно, вы захотите добавить свой composer.json к вопросу, на случай, если есть старый пакет, который переопределяет часть symfony 3.3.   -  person ccKep    schedule 15.06.2017
comment
@cerad, да, я постараюсь как можно скорее ... хорошая отправная точка для отладки   -  person Aerendir    schedule 15.06.2017
comment
@ccKep: это не дубликат, поскольку решение было отключить автоматическую проводку, в то время как я хочу, чтобы она была включена, чтобы понять, в чем проблема, и решить ее ...   -  person Aerendir    schedule 15.06.2017
comment
Может быть, что-то связано с другими сервисами... Я должен исследовать глубже...   -  person Aerendir    schedule 15.06.2017
comment
@Cerad, да, явный вызов метода setContainer решил проблему! Подробнее см. здесь: github.com/symfony/symfony/issues/23200# issuecomment-308837428 Спасибо!!! Если вы опубликуете ответ, я отмечу его как правильный!   -  person Aerendir    schedule 15.06.2017


Ответы (2)


Возможность автоподключения S3.3 упрощает определение контроллеров как сервисов.

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

Однако базовый класс контроллера Symfony предоставляет ряд вспомогательных функций, которые используют около 12 различных сервисов. Было бы действительно больно вводить их по одному. Я как бы думал, что возможность автопроводки может позаботиться об этом за вас, но я думаю, что нет.

Таким образом, вам в основном нужно добавить вызов setContainer в определение вашего сервиса. Что-то типа:

AppBundle\Controller\:
    resource: '../../src/AppBundle/Controller'
    public: true
    [[setContainer, ['@service_container']]]
    tags: ['controller.service_arguments']

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

person Cerad    schedule 15.06.2017

Эта проблема устранена PR #23239 и выпущена в Symfony 3.3.3.

person fracz    schedule 26.07.2017
comment
Да, но я еще не проверял это... Когда я это сделаю, я отмечу это как лучший ответ... Спасибо! - person Aerendir; 27.07.2017