D
D
Dmitry Spirin2014-06-30 19:05:53
symfony
Dmitry Spirin, 2014-06-30 19:05:53

How to store tag alias in the service itself in Symfony2?

There is classic documentation on Working with tags in Symfony2 services .
In my task, with the help of CompilerPass, I transfer all my transport services protegized in the same way to some kind of Provider (TransportChain in the example from the documentation).
It is described like this:

<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>

In the future, from this provider, I can easily get the desired service by alias (...->getTransport($alias)).
The problem is that later I want to identify the received service. Since all services have the same class (instanceof is swept aside), it occurred to me that in each instance of the Example\Transport service, I somehow have to store the value from alias="transport1" in the tag.
But I can't figure out how to correctly transfer it there. Tell me which is better? It will hardly be beautiful if in my CompilerPass I will receive an instance of each transport and network this alias into it, and only then "transfer" it to the provider for saving.
Perhaps I have some general architectural problem. But the point is that I have several transports of the same type. In runtime, I determine the one I need, select it from the provider, do something, and later, for logging, I need to determine which transport I used.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
Igor Makarov, 2014-07-04
@onqu

There are three ways to pass an argument to a service through a configuration :
1. To a constructor
2. Through a setter
3. Directly to a property
It is necessary to determine what to do with services in the future. Using an alias in this case looks redundant, since you can use a unique service id.
Let's use the last example from the page:

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"])
                );
            }
        }
    }
}

Consider two cases.
1. It is necessary to process the data by each transport:
It follows from the documentation that all Transport instances will be in the array of the TransportChain object. Array keys will match aliases. This scheme is fully justified.
2. You need to process data with only one instance of the Transport class:
In this case, the above costs for creating a bunch of objects of the Transport class are not justified, instead, the easiest way is to change the definition of the TransportChain service, feeding it a DI container:
<services>
    <service id="acme_mailer.transport_chain" class="TransportChain">
        <call method="setDI">
             <argument type="service" id="service_container" />
        </call>
    </service>
</services>

Next, using CompilerPass, pass an array with the id of the Transport services to the TransportChain. These actions will allow you to get the desired transport by id directly from the service container.
$transports = array_keys($container->findTaggedServiceIds('acme_mailer.transport' ));
$definition->addMethodCall('setTransports', [ $transports ]);

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question