【问题标题】:Unpack Symfony "tagged Services"解压 Symfony “标记的服务”
【发布时间】:2019-05-21 08:18:54
【问题描述】:

我有一个接受多个Consumer 实现作为构造函数参数的类。

我想通过 Symfony DI 容器“填写”我所有的消费者。 我尝试了注入标记的服务。

final class SynchronousMessageDispatcher implements MessageDispatcher
{
    /**
     * @var Consumer[]
     */
    private $consumers;

    public function __construct(Consumer ...$consumers)
    {
        $this->consumers = $consumers;
    }
}

所以我尝试像这样在services.yml 中标记服务:

services:
    _instanceof:
        EventSauce\EventSourcing\Consumer:
            tags: ['eventsauce.consumer']

然后像这样注入它:

eventsauce.message_dispatcher:
    class: EventSauce\EventSourcing\SynchronousMessageDispatcher
    arguments: [!tagged eventsauce.consumer]

现在我收到以下错误:

传递给 EventSauce\EventSourcing\SynchronousMessageDispatcher::__construct() 的参数 1 必须实现接口 EventSauce\EventSourcing\Consumer,给定 Symfony\Component\DependencyInjection\Argument\RewindableGenerator 的实例

我完全理解为什么。有没有办法解压服务

换句话说:是否可以以某种方式修改[!tagged eventsauce.consumer]。还是 ...$consumers 语法与 Symfony 中的标记服务注入不兼容。

不要误会我的意思。我知道我自己可以轻松实现MessageDispatcher。只是想知道;-)

【问题讨论】:

标签: symfony dependency-injection


【解决方案1】:

我原来的解决方案

正如“Tomáš Votruba”所说,您必须重写自己的 !tagged 功能。例如!tagged-variadic.

这对我来说不值得。我宁愿使用iteratable 来实现这个类(“nifr”解释了它的好处,谢谢)。

为了进一步阅读,symfony/symfony#23608有一个已关闭的问题

我的新解决方案

我使用了Argument unpackingDelegation pattern 来使用库为我的标记服务提供的类。

工作 :-) 万岁。

final class TaggedMessageDispatcher implements MessageDispatcher {
    public function __construct(iterable $consumers)
    {
        $this->dispatcher = new SynchronousMessageDispatcher(... $consumers);
    }

    public function dispatch(Message ...$messages): void
    {
        $this->dispatcher->dispatch(... $messages);
    }
}

【讨论】:

    【解决方案2】:

    您在这里使用了错误的类型提示。

    使用[!tagged <tag>] 语法将注入单个iterable - 而不是splat 运算符所期望的未定义数量的参数。

    您实际上是在此处使用 splat (...$arguments) 运算符将多个 Consumer 对象作为参数输入。

    所以你的问题的答案是: splat 运算符与[!tagged ..] 语法不兼容。 当使用像[!tagged-call_user_func ..] 这样的新符号时,您确实需要编写自己的注入类型来拆分标记的服务。

    这就是说收集对象列表并没有什么意义,将它们提取为函数参数只是为了让 PHP 将它们重新放回列表中。不过,我在代码清洁度方面得到了你的想法。

    另一个限制是您不能将多个可变参数传递给函数。所以...

    public function __construct(Alpha ...$alphas, Beta ...$betas)
    

    ...不可能。

    允许您保留集合的类型提示的可能解决方案/解决方法如下:

    final class SynchronousMessageDispatcher implements MessageDispatcher
    {
        /**
         * @var Consumer[]
         */
        private $consumers;
    
        public function __construct(iterable $consumers)
        {
            foreach($consumers as $consumer) {
              assert($consumer instanceof Consumer, \InvalidArgumentException('..'));
            }
    
            $this->consumers = $consumers;
        }
    }
    

    【讨论】:

    • 感谢您的详细解释。我知道失败的原因。而且我不是“splead”的粉丝,但我正在使用它的图书馆使用它。我可以自己实现接口,但希望能以某种方式解决它。在您的帮助下,我发现了一个问题。 github.com/symfony/symfony/issues/23608 我将简单地编写自己的“MessageDispatcher”实现。
    猜你喜欢
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多