【问题标题】:How to inject the @request into a service?如何将@request 注入服务?
【发布时间】:2012-02-17 17:42:50
【问题描述】:

当我尝试将@request 注入到我的任何服务中时,我会收到以下异常:

ScopeWideningInjectionException:检测到范围扩大注入: 定义“service.navigation”引用了服务“请求” 属于较窄的范围。一般来说,两者都比较安全 将“service.navigation”移动到“请求”范围或依赖 通过注入容器本身并请求提供者模式 每次需要时,服务都会“请求”。在罕见的特殊情况下 但是这可能不是必需的,那么您可以将引用设置为 strict=false 以消除此错误。

最好的方法是什么?我应该尝试设置这个strict=false 以及如何设置,还是不应该注入请求服务,而是每次调用我需要的函数时通过我的控制器将其传递给服务?

其他可能性是注入内核并从那里获取它,但在我的服务中我只使用@router 和@request,因此注入整个内核是不合理的。

【问题讨论】:

    标签: symfony service dependency-injection


    【解决方案1】:

    在 Symfony 2.4 中,这已经改变了。现在,您可以注入“request_stack”服务了。

    例如:

    use Symfony\Component\HttpFoundation\RequestStack;
    
    class MyService
    {
    
        protected $request;
    
        public function setRequest(RequestStack $request_stack)
        {
            $this->request = $request_stack->getCurrentRequest();
        }
    
    }
    

    在你的 config.yml 中:

    services:
        my.service:
            class: Acme\DemoBundle\MyService
            calls:
                - [setRequest, ["@request_stack"]]
    

    完整文档在这里:http://symfony.com/blog/new-in-symfony-2-4-the-request-stack

    【讨论】:

    • +1 表示该解决方案并链接到相应的 symfony 博客文章。
    • 这里的问题是,这是在de构造函数之后执行的,所以如果你需要在构造函数中请求这个解决方案是不完整的
    • @K.Weber 您可以将request_stack 注入到构造函数操作中,或者在构造函数执行之前(例如,在kernel.request 侦听器中)。
    • @Blowski 如果您注入请求堆栈,您应该将请求堆栈保留在 MyService 中,而不是请求自身。每当您需要请求时,请致电Request#getCurrentRequest(或getMasterRequest
    • 注入的@request_stack 应该是像'@request_stack' 这样的撇号,否则它是无效的YAML
    【解决方案2】:

    我认为官方文档的内容可能存在一些误解。在大多数情况下,您确实希望使用 service 元素上的 scope="request" 属性直接注入请求。这使得范围扩大消失了。

    <service 
        id="zayso_core.openid.rpx" 
        class="Zayso\CoreBundle\Component\OpenidRpx" public="true" scope="request">
    

    或在 yml

    zayso_core.openid.rpx: 
        class: Zayso\CoreBundle\Component\OpenidRpx
        public: true
        scope: request
    

    仅在特定的特殊情况下,例如需要注入容器的 Twig 扩展。

    关于作用域的页面中甚至没有提到内核。注入内核(在概念上)比注入容器要糟糕得多。

    更新:对于 S2.4 和更新版本,请使用下面的@Blowski 的答案。

    【讨论】:

    • 这听起来很有趣,很抱歉,你能用 YAML 重写它吗?我尝试了几个这样的声明(在其他网站上找到),但我似乎无法正确。
    • 效果很好,在 YAML 中会是这样的:service.sample: class: Company\SiteBundle\Services\Sample arguments: [@request] public: true scope: request
    • 范围还有其他值吗?尝试从 CLI 运行服务
    • @PhillPafford 可能的范围定义为here,但请注意服务范围为deprecated in Symfony 2.8 and removed in Symfony 3
    【解决方案3】:

    注意:这个答案写于 2012 年,当时 Symfony 2.0 已经发布,然后这是一个好方法!请不要再投票了:)


    今天我自己也遇到了同样的问题,所以这是我的 5 美分。根据official documentation,通常不需要将request 注入您的服务中。在您的服务类中,您可以传递kernel 容器(注入它听起来并不是很大的开销),然后像这样访问request

    public function __construct(\AppKernel $kernel)
    {
        $this->kernel = $kernel;
    }
    
    public function getRequest()
    {
        if ($this->kernel->getContainer()->has('request')) {
            $request = $this->kernel->getContainer()->get('request');
        } else {
            $request = Request::createFromGlobals();
        }
        return $request;
    }
    

    当在 CLI 中访问服务时(例如,在单元测试期间),此代码也可以正常工作。

    【讨论】:

    • 是的,这行得通。只需稍作修改:由于kernel -&gt; getContainer() 返回一个 service_container,最好注入它而不是内核(如文档所述)。我已经完成了arguments: [@service_container],它运行良好——无需通过内核。
    • 其实大错特错,使用 Symfony 2.1.x 和 CLI 命令,has 返回true,随后的get('request') 抛出异常。
    • @Gremo 我的答案是大约一年前写的,与 Symfony 2.0 相关,所以你的反对意见不正确 :)
    • 你可以删除你的答案,因为它几乎不相关 :) 保持清洁 :)
    【解决方案4】:

    我发现让服务使用请求服务而不依赖于整个容器并且仍然不需要具有请求范围的最佳方法是创建一个使用容器的 RequestInjector 服务。然后你将它注入到想要使用请求对象的服务中

    class RequestInjector{
    
        protected $container;
    
        public function __construct(Container $container){
    
             $this->container = $container;
       }
    
        public function getRequest(){
    
            return $this->container->get('request');
        }
    }
    
    class SomeService{
    
        protected $requestInjector;
    
        public function __construct(RequestInjector $requestInjector){
    
            $this->requestInjector = $requestInjector;
    
        }
    }     
    

    对于 services.yml

    request_injector:
        class: RequestInjector
        public: false
        arguments: ['@service_container']
    
    some_service:
        class: SomeService
        arguments: ['@request_injector']
    

    【讨论】:

    • 我不喜欢这种方法,你基本上还是会在某个时候注入整个容器并使用 Service Locator 反模式,只是你不再了解它了。我看不出有什么好处!
    【解决方案5】:

    我发现的方法,我确信它可能不是最好的方法(甚至可能不推荐),就是将请求服务定义为合成的。

    编辑:确实,不建议这样做,因为它会禁用范围健全性检查。 这个线程很好地解释了 Symfony 抛出异常的原因: http://groups.google.com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749

    在你的services.xml:

    <service id="request" synthetic="true" />
    
    <service id="my_service" class="......">
        <argument type="service" id="request" />
    </service>
    

    根据 docs,最好将服务放在请求范围内,或者只注入服务容器。

    【讨论】:

    • 此时,作为评论可能比作为答案更好。
    【解决方案6】:

    如果不能直接使用 RequestStack,可以创建工厂服务,使用 RequestStack 返回当前请求。

    # services.yml
    app.request:
        class: Symfony\Component\HttpFoundation\RequestStack
        factory: [ @request_stack, getCurrentRequest ]
    

    然后您可以使用app.request 服务访问当前请求。

    【讨论】:

      【解决方案7】:

      另一种直接注入currentRequest的方式:

      setter 注入:

      calls:
           - ['setRequest', ['@=service("request_stack").getCurrentRequest()']]
      

      或构造函数注入:

      arguments:
           $request: '@=service("request_stack").getCurrentRequest()'
      

      【讨论】:

        【解决方案8】:

        我认为更重要的是专注于获取请求而不是设置它。我会做类似于@Blowski 的解决方案的事情,除了使用吸气剂。这与documentation 的示例非常相似。

        namespace Acme\HelloBundle\Newsletter;
        
        use Symfony\Component\HttpFoundation\RequestStack;
        
        class NewsletterManager
        {
            protected $requestStack;
        
            public function __construct(RequestStack $requestStack)
            {
                $this->requestStack = $requestStack;
            }
        
            protected function getRequest()
            {
                return $this->requestStack->getCurrentRequest();
            }
        
            public function foo()
            {
                $request = $this->getRequest();
                // Do something with the request
            }
        }
        

        还有你的 services.yml 配置文件。

        services:
            newsletter_manager:
                class:     Acme\HelloBundle\Newsletter\NewsletterManager
                arguments: ["@request_stack"]
        

        现在您始终可以确保收到正确的请求,并且您不必担心设置/重新设置请求。

        【讨论】:

          【解决方案9】:

          正如@simshaun 所说的将您的服务置于请求范围内的最佳做法。这使得服务的目的非常明确。

          注意,这将使您的服务在其他范围(例如命令行)中不可用。但是,如果您的服务依赖于请求,则无论如何您都不应该在命令行上使用它(因为命令行上没有可用的请求。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-16
            • 2020-09-09
            • 2016-02-08
            • 2017-02-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多