简体   繁体   中英

Symfony2: Dependency injection reliant on request

Preamble

My Symfony2 application will be accessed from several TLD's. Depending on the TLD I want to use a different swiftmailer mailer. I however failed to dynamically inject the correct mailer despite trying a multitude of approaches (service factory, compiler pass, DI extension, "dynamic alias").

This lead to a fundamental realisation: Dependencies are injected before the container is compiled, the request is available after the container is compiled. Hence there is no way to make dependency injection reliant on the request (and therefore all above-mentioned approaches failed).

Problem

I was told never to pull dependencies, but to always inject them.

To illustrate this further:

I have

  • three swiftmailers : mailer_en, mailer_de, mailer_ie and
  • three domains: domain.co.uk, domain.de and domain.ie

and want to inject the correct swiftmailer into a custom mailer service for FOSUserBundle (or into any other service that needs swiftmailer).

Question

How do I inject the correct dependency if I don't know it until the request is available?

I had two ideas, but not sure as to how suitable they are:

  1. Should I inject some kind of "Mailer Provider"? Still kind of pulling dependencies, isn't it?
  2. Can I use some kind of proxy class, forwarding interaction to the correct emailer?

Or am I completely on the wrong route?

Injecting the request is covered in the documentation . That being said, I think you'll get the most bang for the buck by using a factory .

For future reference, here is the implementation of Peter's answer :

Custom mailer for FOSUserBundle config:

# app/config/config.yml

fos_user:
    # ...
    service:
        mailer: acme.mailer

and

# src/Acme/UserBundle/config/services.xml

<service id="acme.mailer.factory" class="Acme\UserBundle\Service\TwigSwiftMailerFactory" public="false">
    <call method="setContainer">
        <argument type="service" id="service_container" />
    </call>
</service>

<service id="acme.mailer" class="TwigSwiftMailer">
    <factory service="propeo_user.mailer.factory" method="createTwigSwiftMailer" />
    <argument type="service" id="acme.mailer_name_provider" />
    <argument type="service" id="router" />
    <argument type="service" id="twig" />
    <argument type="collection">
        <argument key="template" type="collection">
            <argument key="confirmation">%fos_user.registration.confirmation.template%</argument>
            <argument key="resetting">%fos_user.resetting.email.template%</argument>
        </argument>
    </argument>
</service>

as well as the factory class:

# Acme/UserBundle/Service/TwigSwiftMailerFactory

class TwigSwiftMailerFactory extends ContainerAware
{
    private function getContainer()
    {
        if(!($this->container instanceof ContainerInterface)) {
            throw new \RuntimeException('Container is missing');
        }
        return $this->container;
    }

    public function createTwigSwiftMailer(MailerNameProvider $mailerNameProvider, UrlGeneratorInterface $router, \Twig_Environment $twig, array $parameters)
    {
        $container = $this->getContainer();
        $name = $mailerNameProvider->getMailerName(); // returns mailer name, e.g. mailer_en

        $mailer = $container->get(
            sprintf('swiftmailer.mailer.%s', $name ? $name : 'default')
        );

        $parameters['from_email']['confirmation'] =
            $parameters['from_email']['resetting'] =
                $container->getParameter(
                    sprintf('swiftmailer.mailer.%s.sender_address', $name ? $name : 'default')
                )
        ;

        return new TwigSwiftMailer($mailer, $router, $twig, $parameters);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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