【问题标题】:Symfony2 DI service container priority/flowSymfony2 DI 服务容器优先级/流
【发布时间】:2015-07-27 18:07:53
【问题描述】:

故事

我们以这个请求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 作为依赖项来检查用户是否属于公司,但我不能这样做,因为它返回 null

但它在控制器和大多数其他 DI 地方都能完美运行。

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


我的尝试

我已将范围 app.company_context_request 设置为 request 并尝试从容器中获取它。带有错误的结果。 根据 Symfony 文档,这应该可以工作。


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


编辑:我的目标是始终提供一种服务,可以告诉我当前的公司当然基于子域。

编辑 2This example 几乎是我所需要的,只是在此示例中他们使用用户公司,而我需要来自子域的公司(密钥)。

【问题讨论】:

  • 您是否不需要根据请求在 UserProvider 和 app.current_company 之间创建依赖关系,这将是当前公司,然后检查您的用户是否链接到当前公司?此外,您是否检查了在您的 USerProvider 业务逻辑中,您的请求中是否有正确的参数(例如公司和 tld)?
  • @MeuhMeuh 这就是我正在使用的app.current_company 是公司实体的一个实例。但是由于容器是在发出请求之前编译的,因此当前请求中的company 属性是null。这就是为什么我需要一个关于我对请求的依赖的解决方案。
  • 什么时候返回null?
  • 不是范围问题吗?你试过了吗:
  • 当您通过调用 PHP 或 Twig render(controller('FooBar')) 在子请求中调用服务时,您的公司是 NULL 吗?

标签: symfony dependency-injection request


【解决方案1】:

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

如果您将按照 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>

但是current_company 应该有scope。如果Requestcurrent_company 的初始化时刻未定义,request 将引发异常。或者你可以使用scope=prototype(它会在每次调用时返回新实例)如果你想在它被设置的时候得到current_company,在Request不存在的时候得到null(out范围,cli 调用)。

<service id="app.current_company" class="AppBundle\Entity\Company" scope="request">
    <factory service="app.company_context_request" method="getCompany"/>
</service>

UPD 它根本与服务优先级无关。它与容器编译机制有关。特定服务的编译发生在第一次调用此服务时:显式调用或依赖服务的编译。

Request 对象尚未在RequestStackContainer 中时,您的服务的UserProvider 路由编译(因此调用__construct)发生。

【讨论】:

    【解决方案2】:

    我不太明白你的问题,但是如果你想在 YML 中设置优先级,可以这样做

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

    --- 编辑---

    首先在 linux firefox 下修复您的 ssl 设置“此连接不受信任”,有在线工具可以检查 ssl 是否配置正确。

    作为您问题的第二个答案,您可以设置一些服务或事件侦听器来读取子域名,如果是服务,只需使用 $this->get("serviceName")->getSubdomain()

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

    【讨论】:

    • 我需要一个服务来知道“当前公司”例如:你去https://nike.superawesomedomain.com/我想要路由器的子域名,这样我就可以对照数据库检查它并返回一个@987654324 @(实体)对象。所以没有事件/听众。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多