Задать вопрос

Как правильно сохранить алиас тэга в самом сервисе в Symfony2?

Есть классическая документация по Работе с тэгами в сервисах Symfony2.

В своей задаче помощью CompilerPass я передаю все протегированные однотипным образом свои транспортные сервисы в какой-то Provider (TransportChain в примере из документации).

Описывается примерно так:
<service id="example.test1_transport" class="Example\Transport">
    <tag name="example.transport" alias="transport1"/>
</service>
<service id="example.test2_transport" class="Example\Transport">
    <tag name="example.transport" alias="transport2"/>
</service>


В дальнейшем из этого провайдер я могу легко получить нужный сервис по алиасу (...->getTransport($alias)).

Проблема в том, что позже я хочу полученный сервис идентифицировать. Поскольку все сервисы имеют одинаковый класс (отметается instanceof), в голову пришло, что в каждом экземпляре сервиса Example\Transport я каким-то образом должен сохранить значение из alias="transport1", что в тэге.
Но не могу сообразить, как грамотно туда его передать. Подскажите, как лучше? Вряд ли будет красиво, если в своеем CompilerPass я буду получать инстанс каждого транспорта и сетить в него этот алиас, а уже потом "передавать" на сохранение в провайдер.

Возможно, у меня какая-то общая архитектурная проблема. Но смысл в том, что у меня есть несколько транспортов однотипных. В рантайме я определяю нужный, выбираю его из провайдера, что-то делаю, а позже для логирования мне надо определить, каким-то транспортом я воспользовался.
  • Вопрос задан
  • 2530 просмотров
Подписаться 4 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 1
onqu
@onqu
weasy
Для передачи аргумента в сервис через конфигурацию есть три способа:
1. В конструктор
2. Через сеттер
3. Напрямую в свойство

Необходимо определить, что предстоит делать с сервисами в дальнейшем. Использование алиаса в данном случае выглядит избыточным, так как можно использовать уникальный id сервиса.

Воспользуемся последним примером со страницы:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

class TransportCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('acme_mailer.transport_chain')) {
            return;
        }

        // Получаем определение сервиса для хранения объектов транспорта
        $definition = $container->getDefinition(
            'acme_mailer.transport_chain'
        );

        // Получаем все сервисы транспорта то тегу
        $taggedServices = $container->findTaggedServiceIds(
            'acme_mailer.transport'
        );

       // Задаем инъекцию каждого транспорта в хранилище
        foreach ($taggedServices as $id => $tagAttributes) {
            // Вложенный цикл перебирает все теги каждого сервиса, здесь надо быть аккуратным
            foreach ($tagAttributes as $attributes) {
                $definition->addMethodCall(
                    'addTransport',
                    array(new Reference($id), $attributes["alias"])
                );
            }
        }
    }
}


Рассмотри два случая.

1. Нужно обработать данные каждым транспортом:
Из документации следует, что все экземпляры Transport окажутся в массиве у объекта TransportChain. Ключи массивов будут соответствовать алиасам. Данная схема вполне оправдана.

2. Нужно обработать данные только одним экземпляром класса Transport:
В этом случае выше приведенные затраты на создание пачки объектов класса Transport не оправданы, вместо этого проще всего изменить определение сервиса TransportChain, скармливая ему DI контейнер:

<services>
    <service id="acme_mailer.transport_chain" class="TransportChain">
        <call method="setDI">
             <argument type="service" id="service_container" />
        </call>
    </service>
</services>


Далее, используя CompilerPass передать в TransportChain массив с id сервисов Transport. Данные действия позволят получить нужный транспорт по id прямиком из сервис контейнера.

$transports = array_keys($container->findTaggedServiceIds('acme_mailer.transport' ));
$definition->addMethodCall('setTransports', [ $transports ]);
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы