Symfony2 DI服务容器优先级/流量

Sne*_*nek 9 dependency-injection request symfony

故事

我们以此请求https://nike.awesomedomainname.com/为例.我需要一个可以获取的服务nike.


我现在有什么

我有一个从服务器获取路由器密钥的服务@request_stack.让我们使用公司作为路由器密钥.

我的主要路线如下;

<import host="{company}.domain.{_tld}" prefix="/" schemes="https" resource="@ExampleBundle/Resources/config/routing.xml">
    <default key="_tld">%app_tld%</default>
    <requirement key="_tld">com|dev</requirement>
    <requirement key="company">[a-z0-9]+</requirement>
</import>
Run Code Online (Sandbox Code Playgroud)

所以我写了这个服务来获取密钥并搜索公司;

...

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class CompanyContextRequest {

    protected $request;
    protected $companyRepository;

    public function __construct(RequestStack $requestStack, CompanyRepository $companyRepository) {
        $this->request = $requestStack->getMasterRequest();
        $this->companyRepository = $companyRepository;
    }

    public function getCompany()
    {
        if (null !== $this->request)
        {
            $company = $this->request->attributes->get('company');
            if (null !== $company)
            {
                return $this->companyRepository->findOneBy([
                    'key' => $company
                ]);
            }
        }
    }

    ....

}
Run Code Online (Sandbox Code Playgroud)

和服务xml定义;

<service id="app.company_context_request" class="AppBundle\Request\CompanyContextRequest">
    <argument type="service" id="request_stack"/>
    <argument type="service" id="orm_entity.company_repository"/>
</service>
<service id="app.current_company" class="AppBundle\Entity\Company">
    <factory service="app.company_context_request" method="getCompany"/>
</service>
Run Code Online (Sandbox Code Playgroud)

现在我的问题是,在某些情况下,服务app.current_company不会返回一个Company对象,而是null给出了一个.

比如我有一个自定义的UserProvider具有CompanyContextRequest作为依赖检查,如果用户属于公司,但我不能这样做,因为它返回null.

但它在控制器和大多数其他DI位置都能很好地工作.

有什么建议?是否有像活动订阅者这样的服务的优先级?


我试过的

我已将范围设置app.company_context_requestrequest并尝试从容器中获取它.结果不对.根据Symfony文档,这应该工作.


我运行最新的稳定版Symfony v2.7.3


编辑:我的目标是随时提供服务,告诉我当前的公司是基于子域的.

编辑2:这个例子几乎是我需要的,除了在这个例子中他们使用用户公司,我需要来自子域的公司(密钥).

ori*_*nal 3

app.company_context_request您的服务本质上是合成的问题- 您无法编译,因为此时没有请求数据。

如何将实例注入容器

为什么它在某些地方有效?

它适用于调用范围内的服务的情况:将合成服务Request插入服务容器并request激活范围后。request范围设置和RequestStack填充发生在\Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel::handle方法及其父方法中。

UserProvider不起作用,因为它的初始化发生在request范围之外:在Request服务激活之前或应用程序离开范围之后。

如何预防呢?

  1. 将服务保存RequestStack到服务属性并不在构造函数中而是在getCompany方法中获取当前请求 - 在调用构造函数时Request可能是未定义的;

    public function __construct(RequestStack $requestStack, CompanyRepository $companyRepository) 
    {
        // You cannot use request at this point cause at initialization of the service there could be no request yet
        $this->requestStack = $requestStack;
        $this->companyRepository = $companyRepository;
    }
    ...
    public function getCompany()
    {
        $request = $this->requestStack->getMasterRequest();
        if (null !== $request)
    ...
    
    Run Code Online (Sandbox Code Playgroud)
  2. 添加到服务定义范围request并通过另一个服务内部获取它container:当任何服务尝试使该服务超出范围时,它将自动生成异常;您应该注意一些限制:如何使用范围这种方式用于FOS捆绑包,例如FOSOauthServer.

如果您要更改代码,如 1 所示,那么您可以保留服务定义不变

<service id="app.company_context_request" class="AppBundle\Request\CompanyContextRequest">
    <argument type="service" id="request_stack"/>
    <argument type="service" id="orm_entity.company_repository"/>
</service>
Run Code Online (Sandbox Code Playgroud)

current_company应该有scope。如果在初始化时未定义,request则抛出异常。或者,如果您想在它被设置的时刻以及在不存在的时刻(超出范围,cli 调用),您可以使用(它将在每次调用时返回新实例)。Requestcurrent_companyscope=prototypecurrent_companynullRequest

<service id="app.current_company" class="AppBundle\Entity\Company" scope="request">
    <factory service="app.company_context_request" method="getCompany"/>
</service>
Run Code Online (Sandbox Code Playgroud)

UPD 与服务优先级完全无关。这和容器编译机制有关。特定服务的编译发生在首次调用该服务时:显式调用或依赖服务的编译时。

通过UserProvider路由编译您的服务(因此调用 __construct 发生在Request对象尚未RequestStackContainer.