Reputation: 2333
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
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:
Or am I completely on the wrong route?
Upvotes: 1
Views: 207
Reputation: 2333
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);
}
}
Upvotes: 1
Reputation: 105918
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.
Upvotes: 1