在控制器的构造函数中自动装配的 Symfony 4.4 服务不会从服务定义 (setContainer) 中获得调用 [英] Symfony 4.4 service autowired in controller's constructor doesn't get call from service definition (setContainer)

查看:23
本文介绍了在控制器的构造函数中自动装配的 Symfony 4.4 服务不会从服务定义 (setContainer) 中获得调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前言:请忽略注入整个容器和其他不干净的东西,我只是想展示不起作用的例子,这是在重构之前.

Foreword: please disregard injecting the whole container and other unclean things, I just wanted to show not-working example, this is before refactor.

我在 YAML 中定义了一个服务:

I have a service defined in YAML:

app.service.my_service:
    class: App\Services\MyService
    public: true
    calls:
        - [setContainer, ['@service_container']]

我的服务的一部分:

class MyService implements ContainerAwareInterface
{
    /** @var ContainerInterface */
    private $container;

    /** @var EntityManagerInterface */
    private $em;

    public function setContainer(?ContainerInterface $container = null)
    {
        $this->container = $container;
        $this->em = $container->get('doctrine')->getManager();
    }

然后我有一个控制器,它在构造函数中自动装配了该服务,而不是从容器中实例化:

Then I have a controller, which have that service autowired in constructor, and not instantiaded from container:

class MyController
{
    /**
     * @var MyService
     */
    private $my_service;

    function __construct(MyService $my_service) {
        $this->my_service = $my_service;
    }

虽然它实际上自动装配服务本身,它完全忽略了 setContainer 调用,所以我最终得到了空容器.
我想避免在任何地方调用 $this->get('app.service.my_service') 并且我不能简单地在控制器的构造函数中调用它一次,或者调用 setContainer 在自动装配服务的构造函数中,因为那时容器是空的.
我有机会以干净的方式做到这一点吗?

While it actually autowires service itself, it completely ignores the setContainer call, so I end up with empty container.
I wanted to avoid calling $this->get('app.service.my_service') everywhere and I can't simply call it once in controller's contructor, or call setContainer in the constructor on autowired service, because at that time container is empty.
Any chances I can do it in a clean way?

推荐答案

既然你已经知道注入容器是一个坏主意,那么我想我就不给你讲课了.我还没有遇到过这个特殊问题,但您可以尝试使用鲜为人知的@required"容器指令,看看它是否有帮助.

Since you already know injecting the container is a bad idea then I guess I'll spare you the lecture. I have not encountered this particular issue but you can try using the little known '@required' container directive and see if it helps.

/** @required */
public function setContainer(ContainerInterface $container)
{
        $this->container = $container;
        $this->em = $container->get('doctrine.orm.default_entity_manager);
}

这更像是猜测而不是答案,但很容易尝试.

This is more of a guess than an answer but it is easy to try.

更新:它奏效了.很酷.

Update: It worked. Cool.

我只想补充一点,在大多数情况下,应该将依赖项注入到构造函数中.这避免了部分初始化的服务.另一方面,只要您是通过容器生成服务,就不是问题.

I just wanted to add that, in most cases, dependencies should be injected into the constructor. This avoids having partially initialized services. On the other hand, as long as you are generating your services via the container then it is not really a problem.

我发现它在特性方面非常有用.例如:

I find it to be very useful in traits. For example:

trait RouterTrait
{
    private RouterInterface $router;

    /** @required */
    public function setRouter(RouterInterface $router)
    {
        $this->router = isset($this->router) ? $this->router: $router;
    }
    // These are just copied from AbstractController
    protected function generateUrl(
        return $this->router->generate(...
    protected function redirectToRoute(
#
class SomeService
    use RouterTrait;
    public function someMethod()
        $this->generateUrl('''

所以如果一个服务需要做一些路由,那么他们只需要使用路由器特性,路由器就会自动注入.保存将样板代码添加到服务的构造函数.我确实在 setRouter 周围设置了一个保护装置,因此它只能被有效调用一次,从而消除了使用 setter 进行依赖的另一个问题.

So if a service needs to do some routing then they just need to use the router trait and the router is automatically injected. Saves adding boilerplate code to the service's constructor. I do put a guard around setRouter so it can only be effectively called once which eliminates another concern about using setters for dependencies.

更新#2:原始代码的问题是 MyService 是用 app.service.my_service 的服务而不是类名定义的.Autowire 实际上生成了第二个 MyService 并注入了它,因为 autowire 会查找与类名匹配的服务 ID.将服务定义更改为:

Update #2: The problem with the original code is that the MyService was defined with a service is of app.service.my_service and not the class name. Autowire actually generated a second MyService and injected it since autowire looks for service ids that match the class name. Changing the service definition to:

App\Services\MyService:
    public: true
    calls:
        - [setContainer, ['@service_container']]

也可以.或者明确地将 app.service.my_service 注入控制器.

Would have also worked. Or explicitly injecting app.service.my_service into the controller.

这篇关于在控制器的构造函数中自动装配的 Symfony 4.4 服务不会从服务定义 (setContainer) 中获得调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆