可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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:
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']
回答4:
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).
回答5:
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.
回答6:
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.
回答7:
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.
回答8:
another way to inject currentRequest
directly:
setter injection:
calls:
- ['setRequest', ['@=service("request_stack").getCurrentRequest()']]
or constrauctor injection:
arguments:
$request: '@=service("request_stack").getCurrentRequest()'
回答9:
As @simshaun states its best practice to place your service in the request scope. This makes the purpose of the service quite clear.
Note that this will make your service unavailable in other scopes such as the command line. However if your service relies upon the request, you should not be using it on the command line anyway (because there is no request available on the command line.