Laravel: Возможно ли отправлять уведомление с задержкой, но динамически изменять настройки smtp?

Я разрабатываю Multi Tenant (несколько баз данных) с Laravel v5.7, и мне удается отправлять электронные письма очереди.

В некоторых конкретных ситуациях я хотел бы отправлять уведомления по запросу с «задержкой», как в руководстве Уведомления по запросу, но сообщающие о настройках SMTP, которые следует использовать перед отправкой.

Я разработал класс, который изменяет значения config().

приложение/арендатор/SmtpConfig.php

class SmtpConfig
{
    public static function setConnection(SmtpConta $conta = null)
    {
        // get connection default settings
        $config = config()->get("mail");

        // populate connection default settings
        foreach ($config as $key => $value) {
            if ( $key == 'host' )      { $config[$key] = $conta->mail_host ?? $config[$key]; }
            if ( $key == 'from' )      { $config[$key] = [
                'address' => ( $conta->mail_host === 'smtp.mailtrap.io' ) ? $config[$key]['address'] : $conta->mail_username,
                'name' => $conta->conta ?? $config[$key]['name']
            ]; }
            if ( $key == 'username' )  { $config[$key] = $conta->mail_username ?? $config[$key]; }
            if ( $key == 'password' )  { $config[$key] = !empty($conta->mail_password) ? $conta->mail_password : $config[$key]; }
        }

        $config['encryption'] = ( $conta->mail_host === 'smtp.mailtrap.io' ) ? null : 'ssl';

        // set connection default settings
        config()->set("mail", $config);
    }

}

... и я вызываю этот класс SmtpConfig в уведомлении:

/**
  * Create a new notification instance.
  *
  * @param $conta
  * @param $subject
  * @return void
  */
  public function __construct(SmtpConta $conta = null, $subject = null)
  {
        $this->conta = $conta;
        $this->subject = $subject;

        $when = \Carbon\Carbon::now()->addSecond(100);

        $this->delay($when);

        app(\App\Tenant\SmtpConfig::class)::setConnection($this->conta);
  }

Я могу успешно отправить «отложенное» уведомление, но, по-видимому, оно всегда использует значения по умолчанию для файла .env.

Теперь я не уверен, имеет ли смысл то, где я вызываю класс, или даже как я могу сообщить уведомлению, какую конфигурацию SMTP он должен использовать.


person Magno Alberto    schedule 17.02.2019    source источник


Ответы (2)


В настоящее время я сталкиваюсь с аналогичной проблемой в кодовой базе Laravel 5.2 с использованием библиотеки резервного копирования уведомлений.

Это пример моего решения, похожего на предложение Кита Лунга. Мы просто расширяем класс Illuminate\Notifications\Channels\MailChannel и переопределяем метод send().

Вам нужно будет определить конфигурацию SMTP по получателям или объектам уведомлений, поэтому вам нужно будет отредактировать мой пример по мере необходимости.

Также это предполагает, что ваше приложение использует Swift_Mailer по умолчанию, поэтому YMMV...

<?php

declare (strict_types = 1);

namespace App\Notifications\Channels;

use Illuminate\Notifications\Channels\MailChannel;
use Illuminate\Notifications\Notification;

class DynamicSmtpMailChannel extends MailChannel
{
    /**
     * Send the given notification.
     *
     * @param  mixed  $notifiable
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return void
     */
    public function send($notifiable, Notification $notification)
    {
        //define this method on your model (note $notifiable could be an array or collection of notifiables!)
        $customSmtp = $notifiable->getSmtpConfig(); 

        if ($customSmtp) {
            $previousSwiftMailer = $this->mailer->getSwiftMailer();

            $swiftTransport = new \Swift_SmtpTransport(
                $customSmtp->smtp_server, 
                $customSmtp->smtp_port,
                $customSmtp->smtp_encryption
            );
            $swiftTransport->setUsername($customSmtp->smtp_user);
            $swiftTransport->setPassword($customSmtp->smtp_password);

            $this->mailer->setSwiftMailer(new \Swift_Mailer($swiftTransport));
        }

        $result = parent::send($notifiable, $notification);

        if (isset($previousSwiftMailer)) {
            //restore the previous mailer
            $this->mailer->setSwiftMailer($previousSwiftMailer);
        }

        return $result;
    }
}

Также может быть полезно хранить эфемерное хранилище пользовательских почтовых программ Swift, чтобы вы могли повторно использовать их в одном и том же вызове/запросе (подумайте о долго работающих воркерах) — например, класс коллекции, где хэш конфигурации smtp используется как ключ предмета.

Желаем удачи.

Изменить: я, вероятно, должен упомянуть, что вам может понадобиться связать это в сервисном контейнере. Что-то вроде этого должно быть достаточно:

// in a service provider
public function register()
{
    $this->app->bind(
        \Illuminate\Notifications\Channels\MailChannel::class
        \App\Notifications\Channels\DynamicSmtpMailChannel::class
    );
}

Или же зарегистрируйте его как отдельный канал уведомлений.

person Harry Lewis    schedule 01.03.2019
comment
в уведомлении с помощью метода без поставщика услуг, как и публичная функция пользовательского драйвера через ($ notified) { return [DynamicSmtpMailChannel:: class]; } - person Ahmed Aboud; 15.11.2019

Я думаю, вы также можете обратиться к этой реализации.

https://stackoverflow.com/a/46135925/6011908

Вы можете выполнить, передав пользовательские конфигурации smtp.

$transport = new Swift_SmtpTransport(
    $customSmtp->host, 
    $customSmtp->port, 
    $customSmtp->encryption
);
person Kit Loong    schedule 17.02.2019
comment
Спасибо за предложение, но я все еще продолжаю использовать значения по умолчанию, читая файл, а не динамически, поскольку мне это нужно, однако, если я действительно могу использовать Swift_SmtpTransport в уведомлении, это может быть частью решения. - person Magno Alberto; 18.02.2019
comment
Добро пожаловать. Пример послужил идеей того, как объявить конфигурацию smtp вне config/mail.php. Вы можете написать свою собственную реализацию, такую ​​как передача объекта CustomSmtp с вашими собственными определенными значениями. - person Kit Loong; 18.02.2019
comment
Кстати... Метод ::newInstance() устарел. - person Magno Alberto; 18.02.2019
comment
К сожалению, безуспешно, видимо, уведомление использует некоторый кеш при запуске php artisan queue: work, оно по-прежнему использует значения из файла .env. Я также пытаюсь использовать Illuminate\Notifications\Events\NotificationSent и Illuminate\Notifications\Events\NotificationSending и зарегистрировать Listener в EventServiceProvider, но пока тоже безуспешно. - person Magno Alberto; 18.02.2019
comment
Возможное решение: $api = resolve('swift.transport'); $api->extend('smtp', function() use ($config) { return (new \Swift_SmtpTransport($config['host'], $config['port'], $config['enc']))->setUsername($config['user'])->setPassword($config['pass']); }); - person Magno Alberto; 20.02.2019