How to inject the @request into a service?

后端 未结 9 2246
Happy的楠姐
Happy的楠姐 2020-12-01 05:53

When I try to inject the @request into any of my services, I get this exception:

ScopeWideningInjectionException: Scope Widening Injection detected:

相关标签:
9条回答
  • 2020-12-01 06:24

    In Symfony 2.4, this has changed. Now, you can inject the 'request_stack' service.

    For example:

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

    In your config.yml:

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

    Full documentation is here: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack

    0 讨论(0)
  • 2020-12-01 06:24

    I think it's more important to focus on getting the request instead of setting it. I would do something similar to @Blowski's solution, except using a getter. This is very similar to the documentation's example.

    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
        }
    }
    

    And your services.yml config file.

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

    Now you're always sure that you're getting the correct request, and you don't have to worry about setting/re-setting the request.

    0 讨论(0)
  • 2020-12-01 06:34

    I think there may have been some misunderstanding about what the official documentation says. In most cases you do want to inject the request directly with a scope="request" attribute on the service element. This makes the Scope Widening go away.

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

    or in yml

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

    It's only in specific special cases such as Twig extensions where you need to inject the container.

    And kernel is not even mentioned in the page on scopes. Injecting the kernel is far worse (conceptually) than injecting a container.

    UPDATE: For S2.4 and newer, use @Blowski's answer below.

    0 讨论(0)
  • 2020-12-01 06:36

    NB: This answer was written back in 2012, when Symfony 2.0 was out and then it was the good way to do! Please don't downvote any more :)


    Today I went through same problem myself, so here are my 5 cents. According to the official documentation it is usually not required to inject request into your services. In your service class you can pass kernel container (injecting it is not a big overhead, as it sounds), and then access request like this:

    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;
    }
    

    This code is also working fine when service is accessed in CLI (eg, during unit-testing).

    0 讨论(0)
  • 2020-12-01 06:37

    The best way i found to make a service use the request service, not rely on the whole container and still not be required to have the request scope, was to make a RequestInjector service which takes the container. then you inject that into the service that wants to use the request object

    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;
    
        }
    }     
    

    for services.yml

    request_injector:
        class: RequestInjector
        public: false
        arguments: ['@service_container']
    
    some_service:
        class: SomeService
        arguments: ['@request_injector']
    
    0 讨论(0)
  • 2020-12-01 06:42

    The way I've found, and I'm sure it's probably not the best way (May not even be recommended), is to define the request service as synthetic.

    Edit: Indeed, this is not recommended, because it disables the scope sanity checks. This thread contains a good explanation of why Symfony is throwing that exception: http://groups.google.com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749

    In your services.xml:

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

    Per the docs, it's better if you place your service in the request scope, or just inject the service container.

    0 讨论(0)
提交回复
热议问题