How to inject the @request into a service?

2020-01-28 03:31发布

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

ScopeWideningInjectionException: Scope Widening Injection detected: The definition "service.navigation" references the service "request" which belongs to a narrower scope. Generally, it is safer to either move "service.navigation" to scope "request" or alternatively rely on the provider pattern by injecting the container itself, and requesting the service "request" each time it is needed. In rare, special cases however that might not be necessary, then you can set the reference to strict=false to get rid of this error.

What is the best way to proceed? Should I try to set this strict=false and how, or should I NOT inject the request service, but rather pass it to the service through my controller each time I call functions I need?

Other possibility would be to inject the kernel and take it from there, but in my service I am using only @router and @request, so injecting the whole kernel would be irrational.

9条回答
我命由我不由天
2楼-- · 2020-01-28 04:03

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

查看更多
做自己的国王
3楼-- · 2020-01-28 04:05

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.

查看更多
乱世女痞
4楼-- · 2020-01-28 04:06

another way to inject currentRequest directly:

setter injection:

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

or constrauctor injection:

arguments:
     $request: '@=service("request_stack").getCurrentRequest()'
查看更多
兄弟一词,经得起流年.
5楼-- · 2020-01-28 04:11

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.

查看更多
Lonely孤独者°
6楼-- · 2020-01-28 04:12

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']
查看更多
可以哭但决不认输i
7楼-- · 2020-01-28 04:12

If you can't use RequestStack directly, you could create a factory service that returns the current request using RequestStack.

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

Then you can access the current request using the app.request service.

查看更多
登录 后发表回答