Back

Swiftmailer with dynamic mail configuration


I'm trying to do something like this, (more information in coments //):

use Setting, View, Mail, Config;
use MyConfig;

class MailerRepository
{
	public function send($email, $subject, $view, $data = [], $attachments = [])
	{
                MyConfig::setMailConfig(); //this is my custom facade, setMailConfig() is a function that fetch configuration from db and set config value throught laravel Config facade, \Config::set('mail.driver','my_value')

                //dd(\Config::get('mail.driver)); -> my_value from databse, everything works fine, config value is updated

		Mail::send($view, $data, function($message) use ($email,$subject,$attachments) 
		{
			[...] //all swiftmailer logic
                        //message sending fails, and logs shows that lartavel is using configuration from app/config/mail.php updated configuration from databse is omitted, so i belive that swiftmailer transport was created earlier in laravel boot
		});
	}
}

--- Update

  1. Is there any way to override default mail configuration (in app/config/mail.php) on-the-fly (e.g. configuration is stored in database) before swiftmailer transport is created?

  2. Is there any way to recreate laravel swiftmailer transport so it can pick up updated config values?

closca replied 3 years ago

I use this:

    $mail=DB::table('mail_settings')->first();
	$config = array(
                'driver' => $mail->driver,
                'host' => $mail->host,
                'port' => $mail->port,
                'from' => array('address' => $mail->from_address, 'name' => $mail->from_name),
                'encryption' => $mail->encryption,
                'username' => $mail->username,
                'password' => $mail->password,
                'sendmail' => '/usr/sbin/sendmail -bs',
                'pretend' => false
            );
    Config::set('mail',$config);
mmswiderski replied 3 years ago

closca said:

I use this:

   $mail=DB::table('mail_settings')->first();
  $config = array(
               'driver' => $mail->driver,
               'host' => $mail->host,
               'port' => $mail->port,
               'from' => array('address' => $mail->from_address, 'name' => $mail->from_name),
               'encryption' => $mail->encryption,
               'username' => $mail->username,
               'password' => $mail->password,
               'sendmail' => '/usr/sbin/sendmail -bs',
               'pretend' => false
           );
   Config::set('mail',$config);

And it works? I'm basically doing the same thing. But one question remain, when do u update this config values? And do you use laravel mail facade to send emails?

mmswiderski replied 3 years ago Solution

I find solution after hours of looking at framework/src/Illuminate witch by the way is quite elegant - congratz Taylor.

There is src/Illuminate/Mail/MailServiceProvider:

	/**
	 * Register the Swift Mailer instance.
	 *
	 * @return void
	 */
	public function registerSwiftMailer()
	{	
		\MyConfig::setUserConfig();

		$config = $this->app['config']['mail'];
	
		$this->registerSwiftTransport($config);

		// Once we have the transporter registered, we will register the actual Swift
		// mailer instance, passing in the transport instances, which allows us to
		// override this transporter instances during app start-up if necessary.
		$this->app['swift.mailer'] = $this->app->share(function($app)
		{
			return new Swift_Mailer($app['swift.transport']);
		});
	}

As you can see code is self explanatory. But be wary of that this is not a facade is just class with static function, registering providers is early step in laravel bootup so you can't use many laravel goodies.

This approach has many drawbacks, e.g. if there is a laravel update in mail provider my 'hack' will be lost.

If any one got better solution, feel free to email me [email protected]

yajra replied 3 years ago

I stumbled upon this issue too and just sharing my solution.

On filters.php add the following code

App::before(function($request)
{
	$mail=DB::table('mail_settings')->first();
	$config = array(
		'driver' => $mail->driver,
		'host' => $mail->host,
		'port' => $mail->port,
		'from' => array('address' => $mail->from_address, 'name' => $mail->from_name),
		'encryption' => $mail->encryption,
		'username' => $mail->username,
		'password' => $mail->password,
		'sendmail' => '/usr/sbin/sendmail -bs',
		'pretend' => false
		);
	Config::set('mail',$config);

    // extract config
	extract(Config::get('mail'));

	// create new mailer with new settings
	$transport = Swift_SmtpTransport::newInstance($host, $port);
	// set encryption
	if (isset($encryption)) $transport->setEncryption($encryption);
	// set username and password
	if (isset($username))
	{
		$transport->setUsername($username);
		$transport->setPassword($password);
	}
    // set new swift mailer
	Mail::setSwiftMailer(new Swift_Mailer($transport));
	// set from name and address
	if (is_array($from) && isset($from['address']))
	{
		Mail::alwaysFrom($from['address'], $from['name']);
	}
});
howtomakeaturn replied 2 years ago

I meet the same problem, too. Thanks for sharing this! I think I should not use dynamic config setting.

palamike replied 1 year ago

Because I got ideas from this thread.I just want to share my solutions. I've using Laravel 5.1 and just solve this problem. My code may not beautiful, but I want to share the way I solve this problem. My solution is to have one function execute before send mail. I've tested and it's just work.

Here, this is the function.


//$configs variable is an array which keep your mail setting config.

public function overrideMailerConfig($configs){

        Config::set('mail.driver',$configs['driver']);
        Config::set('mail.host',$configs['host']);
        Config::set('mail.port',$configs['port']);
        Config::set('mail.username',$configs['user']);
        Config::set('mail.password',$configs['passwd']);
        Config::set('mail.sendmail',$configs['sendmail']);

        $app = App::getInstance();

        $app['swift.transport'] = $app->share(function ($app) {
            return new TransportManager($app);
        });

        $mailer = new \Swift_Mailer($app['swift.transport']->driver());
        Mail::setSwiftMailer($mailer);
    }
jaystabins replied 1 year ago

palamike said:

Because I got ideas from this thread.I just want to share my solutions. I've using Laravel 5.1 and just solve this problem. My code may not beautiful, but I want to share the way I solve this problem. My solution is to have one function execute before send mail. I've tested and it's just work.

Here, this is the function.


//$configs variable is an array which keep your mail setting config.

public function overrideMailerConfig($configs){

       Config::set('mail.driver',$configs['driver']);
       Config::set('mail.host',$configs['host']);
       Config::set('mail.port',$configs['port']);
       Config::set('mail.username',$configs['user']);
       Config::set('mail.password',$configs['passwd']);
       Config::set('mail.sendmail',$configs['sendmail']);

       $app = App::getInstance();

       $app['swift.transport'] = $app->share(function ($app) {
           return new TransportManager($app);
       });

       $mailer = new \Swift_Mailer($app['swift.transport']->driver());
       Mail::setSwiftMailer($mailer);
   }

Where is this set?


Sign in to participate in this thread!



We'd like to thank these amazing companies for supporting us