Sending from multiple Mailgun domains using Laravel Mail facade

I used Macros to add dynamic configuration. I don't remember if this can be done in Laravel 4 but works on 5.

Register macro in service provider (AppServiceProvider)

public function boot()
{
    Mail::macro('setConfig', function (string $key, string $domain) {

        $transport = $this->getSwiftMailer()->getTransport();
        $transport->setKey($key);
        $transport->setDomain($domain);

        return $this;
    });
}

Then I can use like this:

\Mail::setConfig($mailgunKey, $mailgunDomain)->to(...)->send(...)

In your case

\Mail::setConfig($mailgunKey, $mailgunDomain)->to(...)->queue(...)

Switching the configuration details of the Laravel Mailer at runtime is not that hard, however I don't know of any way it can be done using the Mail::queue facade. It can be done by using a combination of Queue::push and Mail::send (which is what Mail::queue does anyway).

The problem with the Mail::queue facade is that the $message parameter passed to the closure, is of type Illuminate\Mail\Message and we need to modify the mailer transport, which is only accessible through the Swift_Mailer instance (and that is readonly within the Message class).

You need to create a class responsible for sending the email, using a Mailgun transport instance that uses the domain you want:

use Illuminate\Mail\Transport\MailgunTransport;
use Illuminate\Support\SerializableClosure;

class SendQueuedMail {

    public function fire($job, $params)
    {
        // Get the needed parameters
        list($domain, $view, $data, $callback) = $params;

        // Backup your default mailer
        $backup = Mail::getSwiftMailer();

        // Setup your mailgun transport
        $transport = new MailgunTransport(Config::get('services.mailgun.secret'), $domain);
        $mailer = new Swift_Mailer($transport);

        // Set the new mailer with the domain
        Mail::setSwiftMailer($mailer);

        // Send your message
        Mail::send($view, $data, unserialize($callback)->getClosure());

        // Restore the default mailer instance
        Mail::setSwiftMailer($backup);
    }

}

And now you can queue emails like this:

use Illuminate\Support\SerializableClosure;

...

Queue::push('SendQueuedMail', ['domain.com', 'view', $data, serialize(new SerializableClosure(function ($message)
{
    // do your email sending stuff here
}))]);

While it's not using Mail::queue, this alternative is just as compact and easy to read. This code is not tested but should work.


This works in Laravel 5.4:

// Get the existing SwiftMailer
$swiftMailer = Mail::getSwiftMailer();

// Update the domain in the transporter (Mailgun)
$transport = $swiftMailer->getTransport();
$transport->setDomain('YOUR-DOMAIN.HERE');

// Use the updated version
$mailer = Swift_Mailer::newInstance($transport);
Mail::setSwiftMailer($mailer);