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

时间:2015-07-27 18:07:54

标签: symfony dependency-injection request

故事

我们以此请求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>

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

...

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

    ....

}

服务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>

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

例如,我有一个自定义UserProvider,其CompanyContextRequest作为依赖项来检查用户是否属于公司,但我不能这样做,因为它返回{{1} }。

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

有什么建议吗?对事件订阅者这样的服务有优先权吗?

我尝试了什么

我已将范围null设置为app.company_context_request,并尝试从容器中获取它。结果不对。 根据Symfony文档,这应该有用。

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

修改:我的目标是始终提供一项服务,告诉我当前的公司是基于子域的。

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

2 个答案:

答案 0 :(得分:3)

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

How to Inject Instances into the Container

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

适用于在范围内调用服务的情况:合成服务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)
    ...
    
  2. 添加到服务定义范围request并在另一个服务中通过container获取它:当任何服务试图使其超出范围时,它将自动生成异常;您应该注意一些限制:How to Work with Scopes这种方式在FOS包中使用,例如FOSOauthServer

  3. 如果您将更改代码,则可以将服务定义保留为

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

    current_company应该有scoperequest如果在Request初始化时未定义current_company,则抛出异常。或者你可以使用scope=prototype(它会在每次调用时返回新实例)如果你希望在设置时获得current_company并在{{1}时获得null不存在(超出范围,cli调用)。

    Request

    <强> UPD 它与服务优先级无关。它与容器编译机制有关。在第一次调用此服务时会对特定服务进行编译:显式调用或依赖服务的编译。

    使用<service id="app.current_company" class="AppBundle\Entity\Company" scope="request"> <factory service="app.company_context_request" method="getCompany"/> </service> 路由编译服务(因此调用 __ construct )发生在UserProvider对象尚未进入RequestRequestStack

答案 1 :(得分:2)

我真的不明白你的问题,但如果你想在YML中设置优先级就像这样

app.locale_listener:
    class: Erik\AppBundle\Service\LocateListener
    arguments: ["%kernel.default_locale%"]
    tags:
        - { name: kernel.event_subscriber, priority: 17 }

---编辑---

首先在linux firefox下修复你的ssl设置“This Connection is Untrusted”,有一些在线工具可以检查ssl是否配置正确。

作为您问题的第二个答案,您可以设置一些将读取子域名的服务或事件监听器,如果服务只使用$ this-&gt; get(“serviceName”) - &gt; getSubdomain()

如何获得子域PHP function to get the subdomain of a URL