0

I have many services which differ very little - swiftmailer services:

swiftmailer:
    default_mailer: default
    mailers:
        default:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_act:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_ACT)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_bbi:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_BBI)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_can:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_CAN)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_ffi:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_FFI)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_flip:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_FLIP)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_hscg:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_HSCG)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_ibn:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_IBN)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_pci:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_PCI)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_vop:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_VOP)%'
            spool:
                type: '%swiftmailer.spool.type%'
        spool_vue:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_VUE)%'
            spool:
                type: '%swiftmailer.spool.type%'

        no_spool_act:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_ACT)%'
        no_spool_bbi:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_BBI)%'
        no_spool_can:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_CAN)%'
        no_spool_ffi:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_FFI)%'
        no_spool_flip:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_FLIP)%'
        no_spool_hscg:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_HSCG)%'
        no_spool_ibn:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_IBN)%'
        no_spool_pci:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_PCI)%'
        no_spool_vop:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_VOP)%'
        no_spool_vue:
            url: '%env(CONFIG__PARAMETERS__MAILER_URL_VUE)%'

So there is probably 16 services. I need to get them by code like VOP, VUE, etc during execution, just before sending mail.

Service id looks like this: swiftmailer.mailer.spool_act

So simplest way would be to create one class with all those 16 injected services. But does not look nice.

Could split them into 2 classes - spool and no spool. Then each would have 8 injections. Still not nice, plus there is no guarantee that there will be no need for more later.

One guy suggested tagging services, and inject collection by tags from here: https://symfony.com/doc/current/service_container/tags.html#reference-tagged-services

I have tried but still not found good solution without overengineering. One idea is to create 16 classes and into each of them inject the swifmailer service like swiftmailer.mailer.spool_act. And then in my class have send method which calls send from swiftmailer.mailer.spool_act. And then tag my created services and inject as in that symfony documentation with iterator. But that looks like doing simple thing too complicated way and with too much code.

What is better solution? There should have been similar problems really for other people. I dont get how I cannot find.

Update

I have tried by one answer with instnacce of. Adjusted it to symfony 3.

<services>
    <instanceof id="Swift_Mailer">
        <tag name="app.mailer"/>
    </instanceof>
</services>

<service id="vop.communication.sender.communication" parent="vop.communication.sender.email.abstract" class="Vop\CommunicationBundle\Sender\CommunicationSender" autowire="true">
<!--            todo change args if using autowire-->
            <argument type="service" id="vop_communication.communication_manager"/>
            <argument type="service" id="sonata.notification.backend"/>
            <argument key="$mailers" type="tagged" tag="app.mailer"/>
        </service>

/**
 * CommunicationSender constructor.
 *
 * @param CommunicationHistoryService $communicationHistoryService
 * @param ManagerInterface            $settingManager
 * @param CommunicationManager        $communicationManager
 * @param BackendInterface            $backend
 */
public function __construct(
    CommunicationHistoryService $communicationHistoryService,
    ManagerInterface $settingManager,
    CommunicationManager $communicationManager,
    BackendInterface $backend,
    iterable $mailers,  
    KernelInterface $kernel
) {
    parent::__construct($communicationHistoryService, $settingManager);

    $this->communicationManager = $communicationManager;
    $this->backend = $backend;
    $this->mailers = $mailers;
    $this->kernel = $kernel;
}

but in that case $this->mailers is empty. Why?

Update:

What I want to do is to have send($code) function which by code chooses mailer and sends email.

Darius.V
  • 737
  • 1
  • 13
  • 29
  • I don't actually see anything in your basically new question that points to iterables. However, my answer below should give you exactly what you are asking for. Did it not work? [Here is a second approach](https://stackoverflow.com/questions/54946647/get-service-via-class-name-from-iterable-injected-tagged-services/54949631#54949631) but it requires a bit more understanding of Service Locators and tags. – Cerad Aug 11 '20 at 12:29

2 Answers2

1

Based on your code I am assuming that you know in advance the id of all the services. In which case a Service Subscriber might work. It is what the framework's AbstractController uses to get it's services. Something like:

class SomeClass implements ServiceSubscriberInterface 
{
    /**
     * This will actually be a service locator 
     * containing only the services you specify
     *
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @required
     */
    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
    }
    public static function getSubscribedServices() : array
    {
        return [
            'ACT' => 'swiftmailer.mailer.spool_act',
            'VOP' => 'swiftmailer.mailer.no_spool_vop',
        ];
    }
    public function someMethod() {
        $vop = $this->container->get('VOP');
        dump(get_class($vop));

There are a number of possible variations of this code but this is the most straight forward one that I know of.

Cerad
  • 48,157
  • 8
  • 90
  • 92
0

Here is a technique you can use to collect all swift mailers. I think that's all you need. If you need to separate these into two separate tags, you will probably need compiler pass.

services:
    _instanceof:
        Swift_Mailer:
            tags: ['app.mailer']
    App\YourServiceThatNeedsAllMailers:
        arguments:
            $mailers: !tagged_iterator app.mailer
gadelat
  • 1,390
  • 1
  • 17
  • 25
  • So far I did not manage make it work by this example, but even if I find a way - how will I identify the service by code? There will be instances collection of those services. But class name will be the same. So by what I can identify it is swiftmailer.mailer.no_spool_act for example? – Darius.V Aug 10 '20 at 07:23
  • You don't describe in description that you need to do something else for different types. As far as I'm concerned, you want to loop over all 16 services and call same method. – gadelat Aug 10 '20 at 11:24
  • Updated description - want a send function which by code picks a mailer service and sends mail. – Darius.V Aug 10 '20 at 11:35