简体   繁体   English

symfony2中的装饰器和依赖项注入

[英]Decorators and dependency injection in symfony2

For example, I have some interface: 例如,我有一些界面:

interface ISmsRepository {
    public function send(SmsMessage $sms);
} 

And I have implementation of this class: 我有这个类的实现:

class SmsRepository implements ISmsRepository{/**some implementation*/}

And now I want to realize decorator for SmsRepository class: 现在我想实现SmsRepository类的装饰器:

class QueueSmsRepository implements ISmsRepository{
    /**
     * @var ISmsRepository
     */
    private $smsRepository;

    public function __construct(ISmsRepository $repository) {
        $this->smsRepository = $repository;
    }

    public function send(SmsMessage $sms) {
        //some addable actions
        $this->smsRepository->send($sms);
    }
}

I can more than one of decorators. 我可以担任多个装饰员。 How can i describe it in config? 我如何在配置中描述它? I tried to do like: 我试图这样做:

<service id="manyrus.sms_bundle.decorated.epochta.sms_repository"
             class="Manyrus\SmsBundle\Lib\Decorators\QueueSmsRepository">
        <argument type="service" id="manyrus.sms_bundle.decorated.epochta.sms_repository"/>
</service>

But i have a error: 但是我有一个错误:

Circular reference detected for service "manyrus.sms_bundle.decorated.epochta.sms_repository", path: "manyrus.sms_bundle.decorated.epochta.sms_repository -> manyrus.sms_bundle.decorated.epochta.sms_repository". 为服务“ manyrus.sms_bundle.decorated.epochta.sms_repository”检测到循环引用,路径:“ manyrus.sms_bundle.decorated.epochta.sms_repository-> manyrus.sms_bundle.decorated.epochta.sms_repository”。

I don't know, what to do. 我不知道该怎么办。 Now, I see only one exit from this situation - create service, that will decorate my SmsRepository. 现在,我只看到这种情况的一个出口-创建服务,它将装饰我的SmsRepository。 Do you have any ideas? 你有什么想法?

You can use decorating-services and no need to use compiler pass. 您可以使用装饰服务,而无需使用编译器传递。

<service id="manyrus_decorated"
         class="Manyrus\SmsBundle\Lib\SmsRepository">
    <argument />
</service>

<service id="manyrus_decorator" class="Manyrus\SmsBundle\Lib\Decorators\QueueSmsRepository" decorates="manyrus_decorated" public="false">
    <argument type="service" id="manyrus_decorator.inner" />
</service>

Then you still work with the orginal service name: 然后,您仍然使用原始服务名称:

manyrus_decorated

You have to manually alter some definitions. 您必须手动更改一些定义。 You can do it in your extension file directly but I would suggest you to use a compiler pass if you want other bundles to be able to add a decorator: 您可以直接在扩展文件中执行此操作,但是如果您希望其他捆绑包能够添加装饰器,我建议您使用编译器遍历

// ../DependencyInjection/Compiler/DecorateSmsRepository.php    

namespace Manyrus\SmsBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

class DecorateSmsRepository implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $repositoryId = 'manyrus.sms_bundle.decorated.epochta.sms_repository';

        if (!$container->hasDefinition($repositoryId)) {
            return;
        }

        $definition = $container->getDefinition($repositoryId);

        // Retrieve the ids of the services tagged with "manyrus.sms_bundle.sms_repository.decorator".
        $taggedServices = $container->findTaggedServiceIds(
            'manyrus.sms_bundle.sms_repository.decorator'
        );

        foreach ($taggedServices as $id => $attributes) {
            // Replace the first argument passed to the constructor by the decorator.
            $definition->replaceArgument(0, new Reference($id));

            // Chain the decorator mechanism.
            $definition = $container->getDefinition($id);
            $repositoryId = $id;
        }

        // Set an alias on the last decorator (this will be the service you want to use).
        $container->setAlias(
            'manyrus.sms_bundle.decorated.epochta.decorated_sms_repository',
            new Alias($repositoryId, false)
        );
    }
}

The definition of your services should be: 服务的定义应为:

<service id="manyrus.sms_bundle.decorated.epochta.sms_repository"
         class="Manyrus\SmsBundle\Lib\SmsRepository">
    <argument />
</service>

<service id="manyrus.sms_bundle.decorated.epochta.sms_repository.decorator.queue"
         class="Manyrus\SmsBundle\Lib\Decorators\QueueSmsRepository">
    <argument />
    <tag name="manyrus.sms_bundle.sms_repository.decorator" />
</service>

Last thing to do is to register your compiler pass in your bundle file: 最后要做的是在捆绑文件中注册编译器传递:

// ../ManyrusSmsBundle.php

namespace Manyrus\SmsBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Manyrus\SmsBundle\DependencyInjection\Compiler\DecorateSmsRepository;

class DaOAuthClientBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(new DecorateSmsRepository());
    }
}

Then you can work with the service manyrus.sms_bundle.decorated.epochta.decorated_sms_repository . 然后,您可以使用manyrus.sms_bundle.decorated.epochta.decorated_sms_repository服务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM