【问题标题】:Call a method on a service created dynamically by a bundle调用由包动态创建的服务上的方法
【发布时间】:2018-02-24 12:58:26
【问题描述】:

我正在使用m6web_guzzle 捆绑包来注册多个 http 客户端:

m6web_guzzlehttp:
    clients:
        myclient:
            timeout: 3
            headers:
                "Accept": "application/json"
            delay: 0
            verify: false

我想在它动态生成的服务上调用一个方法。在这种情况下生成的服务名称是:

@m6web_guzzlehttp.guzzle.handlerstack.myclient

这是我在服务构造函数中所做的:(注入的第三个参数是'@m6web_guzzlehttp.guzzle.handlerstack.myclient')

/**
 * @param array        $parameters
 * @param Client       $client
 * @param HandlerStack $handlerStack
 */
public function __construct(array $parameters, Client $client, HandlerStack $handlerStack)
{
    $this->parameters = $parameters;
    $this->client = $client;
    $this->handlerStack->push(Middleware::retry([$this, 'retryDecider']));
}

到目前为止,它运行良好,但是如何转移我的services.yml 文件中的最后一行(push 调用)?还是另一种更简洁的方法来注册此重试处理程序?

【问题讨论】:

  • 您可以编写一个compiler pass 来获取相关定义并向其添加方法调用。

标签: symfony service dependency-injection yaml


【解决方案1】:

所以之前提到过编译器通行证。这是一种选择。

使用工厂创建实例

但您也可以几乎直接在您的服务定义中表达这一点。我说几乎,因为您将需要某种代码,因为 Symfony 服务定义无法 (AFAIK) 评估为闭包——这正是 Guzzle 中间件所需要的。

我写了这个 services.yml 作为例子:

m6web_guzzlehttp.guzzle.handlerstack.myclient:
    class: GuzzleHttp\HandlerStack
    factory: ['GuzzleHttp\HandlerStack', create]

retry_decider:
    class: MyBundle\RetryDecider
    factory: ['MyBundle\RetryDecider', createInstance]

retry_handler:
    class: GuzzleHttp\Middleware
    factory: ['GuzzleHttp\Middleware', retry]
    arguments:
        - '@retry_decider'

handlerstack_pushed:
    parent: m6web_guzzlehttp.guzzle.handlerstack.myclient
    calls:
        - [push, ['@retry_handler']]

什么是什么?

  • m6web_guzzlehttp.guzzle.handlerstack.myclient - 您的动态服务 - 从示例中删除,因为您已经创建了它。
  • retry_decider - 你的决定者。我们在createInstance 方法中返回一个闭包。如果需要,您可以添加更多参数,只需将参数添加到您的 YML。
  • retry_handler - 这里我们使用我们的决策器来组合中间件
  • handlerstack_pushed - 这里我们将push() 处理程序放入堆栈,使用动态服务作为父服务。

等等 - 我们有动态服务定义的堆栈,但推送了我们的重试中间件。

这里是我们的决策者的来源:

<?php

namespace MyBundle;

class RetryDecider {

    public static function createInstance() {
        return function() {
            // do your deciding here
        };
    }

}

--> 您现在拥有服务handlerstack_pushed,它是完整的堆栈。

配置更多

请注意,您可以将m6web_guzzlehttp.guzzle.handlerstack.myclient 添加到parameters.yml

parameters:
    baseHandlerStackService: m6web_guzzlehttp.guzzle.handlerstack.myclient

然后在handlerstack_pushed上使用它:

handlerstack_pushed:
    parent: "%baseHandlerStackService%"
    calls:
        - [push, ['@retry_handler']]

这样更好;-)

【讨论】:

  • 我在 services.yml 中没有任何错误,但从未调用过 Retrydecider。
  • @COil 好吧,您是否将“handlerstack_pushed”服务传递给正在使用的任何其他服务?如果你不将它连接到任何正在使用的东西(我不知道你在用它做什么),它就不会被调用..
  • 好吧,我不知道这个服务是如何“生活”的,因为它是由捆绑包自动创建的。但是不,我不会手动将其传递给其他服务。似乎它与@asha 答案中提到的错误相同,它可能会在此过程的后期被覆盖。我必须进一步调查。没那么容易。
【解决方案2】:

在你的 bundle 的 Extension.php 文件中,你可以重写 load 方法并添加:

$definition = $container->getDefinition('m6web_guzzlehttp.guzzle.handlerstack.myclient');
$definition->addMethodCall('push', [Middleware::retry([$this, 'retryDecider'])]);

【讨论】:

  • 您好,调用加载函数时,m6web_guzzlehttp.guzzle.handlerstack.myclient 服务尚未创建。 “您请求的服务不存在……”
  • 可能 guzzle 包尚未加载。请添加AppKernel中bundle注册的顺序(registerBundles方法)。我看看。
  • 不,带有此扩展名的捆绑包位于最后位置。
  • 您确定动态创建的服务的名称是m6web_guzzlehttp.guzzle.handlerstack.myclient 并且该服务实际上是在 guzzle 捆绑包注册期间创建的。如果它被创建,它的定义应该可以从 YourExtension.php 中的容器构建器访问
【解决方案3】:

您可以编写一个compiler pass 来获取相关定义并向其添加方法调用。

【讨论】:

  • 我期待一个例子。 :)
  • 您能否指出创建此动态服务的代码,以便我了解您如何通过编译器传递来解决具体问题(然后我会更新我的答案)?
  • 添加了更多细节。
  • 我已经添加了代码,但是没有调用决策函数。如果我从控制器调用相同的代码,它就可以工作。
  • 推入的堆栈似乎在稍后的过程中被覆盖。在扩展中创建它时,它位于位置 5,当在控制器中手动创建时,它位于最后一个位置 7。
猜你喜欢
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
  • 2013-06-18
  • 2012-12-19
  • 1970-01-01
相关资源
最近更新 更多