swiftmailer-mailgun-bundle icon indicating copy to clipboard operation
swiftmailer-mailgun-bundle copied to clipboard

Multi key and domain

Open fyher opened this issue 7 years ago • 16 comments

How I can use , if I want multi domain and key ?

thanks

fyher avatar Mar 28 '17 09:03 fyher

I do not think that is supported. That should be an excellent feature.

Nyholm avatar Mar 29 '17 10:03 Nyholm

it 's possible to move the api key to the file config in param in the mailer class ?

fyher avatar Mar 29 '17 11:03 fyher

I'm not too sure what you mean.

I think we should allow config like:

Mailgun:
  foo:
    Key: xx
    Domain: xx
  Bar:
    Key: yay
    Domain: yay

Nyholm avatar Mar 30 '17 06:03 Nyholm

why not.But this don't working actually ?

fyher avatar Mar 31 '17 04:03 fyher

Can't believe this hasn't been implemented, this is actually a show stopper for us. Our CRM platform hosts multiple sites from the same setup, we need the ability to support multiple domains.

sigh time to have a dig around in the code.

stevechilds avatar Apr 07 '18 09:04 stevechilds

Mailgun itself does in fact support multiple domains per account. If they are managed in the same account it should perfectly be possible to use them.

tehplague avatar Apr 07 '18 13:04 tehplague

Actually, there's a very simple solution - subclass the Transport class! Symfony allows you to specify any service as a transport in the config, doing this allows you to inject other dependencies into the transport constructor, allowing you to change the domain passed through to the underlying Mailgun Transport.

If anyone is interested...

Step 1, create a custom class (customMailgunTransport.php) - I saved this into src/BundleName/SwiftMailer

<?php

namespace foo\WebsiteBundle\SwiftMailer;

use cspoo\Swiftmailer\MailgunBundle\Service\MailgunTransport;
use Mailgun\Mailgun;
use Symfony\Component\DependencyInjection\ContainerInterface;


class customMailgunTransport extends MailgunTransport
{
    public function __construct(ContainerInterface $container, \Swift_Events_EventDispatcher $eventDispatcher, Mailgun $mailgun, $domain)
    {
        parent::__construct($eventDispatcher, $mailgun, $domain);

    }
}

Then create the new service entry in services.yml...

    # Custom SwiftMailer Mailgun Transport - gets credentials from site config.
    swiftmailer.mailer.transport.custom_mailgun:
              class: foo\WebsiteBundle\SwiftMailer\customMailgunTransport
              arguments: [@service_container,'@mailgun.swift_transport.eventdispatcher','@mailgun.library','mydomain.com']

Alter the mail config - in our case we want the mailgun transport to be manually selected in the code... (in config_global.yml) - I can't recall if this is a stock Symfony file or not, it may just be config.yml

swiftmailer:
    default_mailer: default
    mailers:
        default:
            transport: %mailer_transport%
            host:      %mailer_host%
            username:  %mailer_user%
            password:  %mailer_password%
        mailgun:
            transport: custom_mailgun

Then when you want to send via mailgun, you create it with a call like:

$swiftmailer = $this->getContainer()->get('swiftmailer.mailer.mailgun');

The service name is swiftmailer.mailer. and then the mailer name from the config.yml file.

HTH....

stevechilds avatar Apr 08 '18 17:04 stevechilds

it' no possible to override the getConfigTreeBuilder with the information in the DB table ?
i have all information for my site in my CRM in database and it's possible to send this to mailgun ?

fyher avatar Apr 09 '18 08:04 fyher

You cannot (or should not) access your database at the time when you build your container. That means you have to redeploy every time you get a new customer.

What you should do is to create a factory that spits out a new instance of the client each time you call it. That factory itself should check with your database values.

Nyholm avatar Apr 09 '18 08:04 Nyholm

it's no possible to override cspooSwiftmailerMailgunExtension ? and inject in the val with my database ?

fyher avatar Apr 09 '18 08:04 fyher

@fyher - If you take the approach I posted above, you can do what you want to do. You'd load the config in the constructor of the customMailgunTransport class.

stevechilds avatar Apr 09 '18 08:04 stevechilds

  • Its what we do, except we load the config from YML files which in turn are cached in Redis. It saves a trip to the DB on every page load (or in this case, whenever the service is created)

stevechilds avatar Apr 09 '18 08:04 stevechilds

hi i have success :) create the override for te transport

`<?php

namespace AdminBundle\SwiftMailer;

use cspoo\Swiftmailer\MailgunBundle\Service\MailgunTransport; use Mailgun\Mailgun; use Symfony\Component\DependencyInjection\ContainerInterface;

class CustomMailgunTransport extends MailgunTransport {

private $mailgun;
private $event;

public function __construct(ContainerInterface $container, \Swift_Events_EventDispatcher $eventDispatcher, Mailgun $mailgun)
{


   $this->mailgun=$mailgun;
   $this->event=$eventDispatcher;

}

public function choiceDomaine($domain){

    parent::__construct($this->event, $this->mailgun, $domain);

}

}` in file config:

cspoo_swiftmailer_mailgun: key: "key" domain: "domaineA"

the service custom_mailgun: class: AdminBundle\SwiftMailer\CustomMailgunTransport arguments: ['@service_container','@mailgun.swift_transport.eventdispatcher','@mailgun.library']

and in the controller for example:

`$m=$this->get("swiftmailer.mailer"); $mails=$this->get("custom_mailgun")->choiceDomaine("domaineB.com");

    $mail = \Swift_Message::newInstance();
    $mail -> setFrom("[email protected]", "text")
        -> setTo("[email protected]")
        -> setSubject("text")
        -> setBody("it's me ")
        -> setContentType("text/html");


    $m->send($mail);`

in the log of mailgun, I have the email send with domaine B , you can change domaineC etc.. it's work

fyher avatar Apr 09 '18 09:04 fyher

public function choiceDomaine($domain){

    parent::__construct($this->event, $this->mailgun, $domain);

}

Whilst it may work, I'd refactor this so you're not calling the parent's constructor method, perhaps move that functionality into a method called 'init' or something. But glad to see it worked out :) 👍

stevechilds avatar Apr 09 '18 10:04 stevechilds

Actually, there's another way to do this without subclassing the Transport class. When the transport sends the message it checks the headers of the email to see if there is a mailgun domain tag there, if there is it uses it.

So, either set the header each time you create a message, or better still, use a SwiftPlugin to do it for you.

Services.yml:

    swiftmailer.mgDomainSetter:
        class:        path\toYourBundle\SwiftMailer\Plugins\domainSetterPlugin
        tags:
          - { name: swiftmailer.default.plugin }
          - { name: swiftmailer.mailgun.plugin }

Note, you'll probably only need one tag, for the default, but we're using mailgun along side other transports, so you need to specify which mailers to attach the plugin to.

domainSetterPlugin.php

<?php

namespace path\toYourBundle\SwiftMailer\Plugins;
use Monolog\Logger;


class domainSetterPlugin implements \Swift_Events_SendListener
{
    /**
     * Invoked immediately before the Message is sent.
     *
     * @param \Swift_Events_SendEvent $evt
     */
    public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
    {
        $msg = $evt->getMessage();
        // Ok, is it being sent via mailgun, if so, we need to set the domain to the sender's domain.
        // all our domains are on the prefix of mg., so we need to prefix this with the sender's email domain.
        if ($evt->getSource() instanceof MailgunTransport) {
           $sender = $msg->getFrom();
           if (array($sender)) {
               // The array is email_address => name, so we need to get the domain name.
               $sender = implode('',array_keys($sender));

               // Firstly, set the sender param of the mail, this stops Mailgun mangling the sender and firing off spam traps!
               // e.g. the sender ends up being set to Sender: [email protected] in the email message.
               $msg->getHeaders()->addTextHeader('sender',$sender);
               $domain = 'mg.' . substr($sender,strpos($sender,'@')+1);
               $msg->getHeaders()->addTextHeader('mg:domain',$domain);
           }
        }
    }
}

And that should be it! It does rely on your senders being from domains you've got setup in mailgun however, so if this is not the case, this won't work for you as you'll get invalid domains. Hopefully it may help you either as-is or as inspiration!

stevechilds avatar Apr 12 '18 07:04 stevechilds

I prefer to set it dynamically for every message like this:

<?php

use cspoo\Swiftmailer\MailgunBundle\Service\MailgunTransport;

class MyMailer {
    //...

    public function sendMessage(string $mailFrom)
    {
        $message = \Swift_Message::newInstance();
            ->setSubject('foo')
            ->setBody('bar')
            ->setFrom($mailFrom);

        $domain = $this->getDomain();
        if ($domain) {
            $message->getHeaders()->addTextHeader(MailgunTransport::DOMAIN_HEADER, $domain);
        }

        // send message
    }

    //...

    public function getDomain(string $mailFrom): ?string
    {
        $split = explode('@', $this->mailFrom);
        if (isset($split[1])) {
            return $split[1];
        }

        return null;
    }
}

Hope that helps.

petermanser avatar Jul 07 '18 08:07 petermanser