使用 Laravel Mail 外观从多个 Mailgun 域发送

问题描述 投票:0回答:6

我正在使用 Laravel 4 的

Mail::queue()
来发送电子邮件,使用内置的 Mailgun 驱动程序。问题是我希望能够从多个 Mailgun 域发送电子邮件,但域必须在
app/config/services.php
中设置。由于我使用的是
Mail::queue()
,我看不到如何动态设置该配置变量。

有什么办法可以实现我的要求吗?理想情况下,我希望在调用

Mail::queue()
时能够传入域(Mailgun api 密钥对于我想要发送的所有域都是相同的)。

email laravel dns queue mailgun
6个回答
9
投票

我使用

Macros
添加动态配置。我不记得这是否可以在 Laravel 4 中完成,但可以在 5 中完成。

在服务提供商中注册宏(

AppServiceProvider
)

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

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

        return $this;
    });
}

然后我可以这样使用:

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

就你而言

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

5
投票

在运行时切换 Laravel Mailer 的配置细节并不难,但是我不知道有什么方法可以使用

Mail::queue
外观来完成它。可以通过使用
Queue::push
Mail::send
的组合来完成(这就是
Mail::queue
的作用)。

Mail::queue
门面的问题是传递给闭包的
$message
参数是
Illuminate\Mail\Message
类型,我们需要修改邮件程序传输,它只能通过
Swift_Mailer
实例访问(即在
Message
类中只读)。

您需要创建一个负责发送电子邮件的类,使用 Mailgun 传输实例来使用您想要的域:

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);
    }

}

现在您可以像这样对电子邮件进行排队:

use Illuminate\Support\SerializableClosure;

...

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

虽然它没有使用

Mail::queue
,但这种替代方案同样紧凑且易于阅读。该代码未经测试,但应该可以工作。


5
投票

这适用于 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);

2
投票

我的用例与此类似,简而言之,我只是想在运行时自动配置 mailgun 发送域,通过查看消息的 from 地址字段中设置的域(我在发送之前动态设置)

Mail::from(...)->send(...)
)。如果OP将消息中的发件人地址设置为与mailgun发送域匹配,这将解决OP的用例,这可能应该完成。

我的解决方案注册了一个备用 MailgunTransport,它会覆盖内置的 MailgunTransport 并在发送之前设置域。这样我只需要在我的

mail.php
中注册新司机,然后拨打
Mail::send
Mail::queue
即可。

config\mail.php:

'driver' => env('MAIL_DRIVER', 'mailgun-magic-domain')

提供商\MailgunMagicDomainProvider:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

use Illuminate\Mail\Transport\MailgunTransport;
use Swift_Mime_Message;
use Illuminate\Support\Arr;

use GuzzleHttp\Client as HttpClient;

class MailgunMagicDomainProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $swiftTransport = $this->app['swift.transport'];

        $swiftTransport->extend('mailgun-magic-domain', function($app) {
            $config = $app['config']->get('services.mailgun', []);

            $client = new HttpClient(Arr::add(
                Arr::get($config, 'guzzle', []), 'connect_timeout', 60
            ));

            return new MailgunTransportWithDomainFromMessage(
                $client, 
                $config['secret'], 
                $config['domain'] // <- we have to pass this in to avoid re-writing the whole transport, but we'll be dynamically setting this before each send anyway
            );
        });
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {

    }
}

/**
 * Overrides the built in Illuminate\Mail\Transport\MailgunTransport but doesnt pull the
 * mailgun sending domain from the config, instead it uses the domain in the from address 
 * to dynamically set the mailgun sending domain
 */
class MailgunTransportWithDomainFromMessage extends MailgunTransport 
{
    /**
     * {@inheritdoc}
     */
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
    {
        $this->setDomain($this->getDomainFromMessage($message));

        return parent::send($message, $failedRecipients);
    }

    protected function getDomainFromMessage(Swift_Mime_Message $message) 
    {
        $fromArray = $message->getFrom();

        if (count($fromArray) !== 1) {
            throw new \Exception('Cannot use the mailgun-magic-domain driver when there isn\'t exactly one from address');
        }

        return explode('@', array_keys($fromArray)[0])[1];
    }
}

config/app.php:

'providers' => [
    ...
    \App\Providers\MailgunMagicDomainProvider::class
],

0
投票

也许这对某人有用,我解决如下;

ServiceProvider
下的boot函数/方法中;

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

        config()->set('services', array_merge(config('services'), [
            'mailgun' => [
                'domain' => $domain,
                'secret' => $key
            ]
        ]));
    });
}

呼叫排队

Mail::setConfig($key, $domain)->to(...)->queue(...)

0
投票

对于 Laravel 9+,您可以使用此代码,因为 swift mailer 不再存在。

app('mail.manager')->setSymfonyTransport(app('mail.manager')
                   ->createSymfonyTransport([
                                 'transport' => 'mailgun',
                                 'secret' => 'paste-secret-key-here',
                                 'domain' => 'your-other-domain',
                               ]));

Mail::to('...')->send(new YourEmailClass);

因此,在发送电子邮件之前,您可以设置此配置。

© www.soinside.com 2019 - 2024. All rights reserved.