如何将@request注入到服务中?
当我尝试将@request注入到我的任何服务中时,出现以下exception:
ScopeWideningInjectionException:范围扩展注入检测:定义“service.navigation”引用属于较窄范围的服务“请求”。 通常,将“service.navigation”移动到范围“request”或者通过注入容器本身来依赖提供者模式并且在每次需要时请求服务“请求”是更安全的。 在罕见的特殊情况下,可能不需要,那么你可以将参考设置为strict = false来摆脱这个错误。
什么是最好的方式进行? 我应该尝试设置这strict=false
和如何,或者我应该不注入请求服务,而是通过我的控制器每次我调用我需要的函数传递给服务?
其他的可能性是注入内核并从中取出,但是在我的服务中,我只使用@router和@request,因此注入整个内核将是不合理的。
谢谢!
我想可能是对官方文件说的有些误解。 在大多数情况下,您确实希望直接在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的答案在下面。
在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
发现服务使用请求服务的最佳方式,不依赖于整个容器而仍然不被要求具有请求范围,而是使用容器进行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']
我发现的方式,我相信它可能不是最好的方法(可能不build议),是将请求服务定义为合成。
编辑:的确,这不是build议,因为它禁用范围健全性检查。 这个线程很好的解释了为什么Symfony抛出这个exception: 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>
根据这些文档 ,最好将您的服务放在请求范围内,或者只是注入服务容器。
注意:这个答案是在2012年Symfony 2.0出来的时候写的,那么这是一个好的方法! 请不要downvote更:)
今天我自己也遇到了同样的问题,所以这里是我的5美分。 根据官方文件 ,通常不需要向您的服务注入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中访问服务时(例如,在unit testing期间),该代码也正常工作。
如果您不能直接使用RequestStack,则可以使用RequestStack创build一个返回当前请求的工厂服务。
# services.yml app.request: class: Symfony\Component\HttpFoundation\RequestStack factory: [ @request_stack, getCurrentRequest ]
然后,您可以使用app.request
服务访问当前请求。
我认为把重点放在获取请求而不是设置它是更重要的。 除了使用getter之外,我会做类似@ Blowski的解决scheme。 这与文档的例子非常相似。
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.ymlconfiguration文件。
services: newsletter_manager: class: Acme\HelloBundle\Newsletter\NewsletterManager arguments: ["@request_stack"]
现在你总是确定你正在得到正确的请求,你不必担心设置/重新设置请求。
正如@simshaun所说的将您的服务放在请求范围内的最佳实践。 这使服务的目的很明确。
请注意 ,这会使您的服务在其他作用域(如命令行)中不可用。 但是,如果您的服务依赖于请求,则不应该在命令行上使用它(因为命令行上没有可用的请求。