【问题标题】:Dependency injection for a bundle controller捆绑控制器的依赖注入
【发布时间】:2019-05-30 02:11:38
【问题描述】:

我有一个简单的、可工作的 SecurityController,我想将它打包成一个包,这样我就可以在我创建的几个基本网站之间共享基本的身份验证功能。在我尝试将我的代码变成一个包之前,一切都按预期工作。

我已经创建了我的包类,一个 Resources/config/routing.xml 文件来声明我的登录和注销路由,在 Resources/views/Security/login.html.twig 中有一个模板,但是下面的类抛出了一个错误.

<!-- Controller/SecurityController.php -->
<?php

namespace JustinVoelker\EssentialSecurityBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class SecurityController extends AbstractController
{
    private $authenticationUtils;

    public function __construct(AuthenticationUtils $authenticationUtils)
    {
        $this->authenticationUtils = $authenticationUtils;
    }

    public function loginAction()
    {
        $error = $this->authenticationUtils->getLastAuthenticationError();

        return $this->render('@EssentialSecurity/Security/login.html.twig', [
            'error' => $error,
        ]);
    }

    ... Comments and additional functions removed for simplicity

}

我进入登录页面时遇到的错误是Controller "JustinVoelker\EssentialSecurityBundle\Controller\SecurityController" has required constructor arguments and does not exist in the container. Did you forget to define such a service?

按照几个不同的示例/教程,我尝试创建一个 services.xml 文件并通过 DependencyInjection/EssentialSecurityExtension.php 加载它,以使 AuthenticationUtils 可用于我的构造函数,但这似乎没有改变任何东西。

<!-- DependencyInjection/EssentialSecurityExtension.php -->
<?php

namespace JustinVoelker\EssentialSecurityBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

class EssentialSecurityExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new XmlFileLoader(
            $container,
            new FileLocator(__DIR__.'/../Resources/config')
        );
        $loader->load('services.xml');
    }
}
<!-- Resources/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="essential_security.controller"
                 class="JustinVoelker\EssentialSecurityBundle\Controller\SecurityController">
            <argument type="service" id="security.authentication_utils"/>
        </service>
    </services>
</container>

我缺少什么让我可以在将代码移动到包中之前在包中使用依赖注入?

如果我只是删除对 AuthenticationUtils 的任何引用(私有属性、整个构造函数及其在 loginAction 中的用法),页面将呈现,但如果没有我在第一名。

旁注,如果我手动将 JustinVoelker\EssentialSecurityBundle\Controller\SecurityController: ~ 添加到我的应用程序主 config/services.xml 文件中,控制器错误就会消失得如此明显,我在我的包中缺少一些东西来完成这项工作。

也许还有另一种方法可以实现我的最终目标,即将最后一个身份验证错误消息返回到登录页面,但我的问题是我缺少什么阻止这种依赖注入像捆绑控制器之前那样工作并且它似乎在我见过的许多例子中都有效。

EDIT 2019-05-30包括我原来的routing.xml的一部分

<route id="essential_security_login" path="/login">
    <default key="_controller">EssentialSecurityBundle:Security:login</default>
</route>

【问题讨论】:

标签: symfony symfony4


【解决方案1】:

看起来您在路由中使用JustinVoelker\EssentialSecurityBundle\Controller\SecurityController,但您的服务名称是essential_security.controller 您应该更改路由或服务定义

您可以添加别名

<!-- Resources/config/services.xml -->
<service id="JustinVoelker\EssentialSecurityBundle\Controller\SecurityController" alias="essential_security.controller"/>

<service id="essential_security.controller" class="JustinVoelker\EssentialSecurityBundle\Controller\SecurityController">
    <argument type="service" id="security.authentication_utils"/>
    <tag name="controller.service_arguments"/>
</service>

或者只是重命名(注意可以省略class参数)

<!-- Resources/config/services.xml -->
<service id="JustinVoelker\EssentialSecurityBundle\Controller\SecurityController">
    <argument type="service" id="security.authentication_utils"/>
    <tag name="controller.service_arguments"/>
</service>

或在您的路由中:

route_name:
    path:     /path
    controller: JustinVoelker\EssentialSecurityBundle\Controller\SecurityController::loginAction
route_name:
    path:     /path
    controller: essential_security.controller::loginAction

取决于您的服务名称

【讨论】:

  • 你已经很接近了。您需要将 controller.service_arguments 标签添加到控制器,以便 Symfony 知道它是一个控制器。虽然您应该根据标准使用服务 ID 的类名,但如果他们没有正确连接,原始发布者将找不到控制器。
  • 绝对需要两者。服务 id 和我在路由中使用的不匹配是第一个问题,然后我也缺少 service_arguments 标记。纠正两者都解决了这个问题。我会赞成这个答案,但会写一个新的答案,其中包含所有必要的更改。
  • 很高兴你能成功。我确信@BoShurik 会回来使用他们回答下的编辑按钮并使用控制器标签对其进行更新。这时候你就可以接受了。
  • @Cerad 感谢您的收获。实际上它并不是真正需要的,因为loginAction 不接受任何参数(标签用于将服务注入控制器的操作)。但是public: true(为controller.service_arguments标签自动添加)对于控制器是强制性的