18

I have created a service provider in my Laravel Application SettingsServiceProvider. This caches the settings table from the database.

$settings = $cache->remember('settings', 60, function() use ($settings)
    {
        return $settings->pluck('value', 'name')->all();
    });

config()->set('settings', $settings);

settings table:

enter image description here

I am able to echo the value from the table like this:

{{ config('settings.sitename') }}  //returns Awesome Images

This works fine on any blade files or controllers in App\Http\Controllers

Problem:

I am trying to echo the value to App\config\mail.php like this:

'driver' => config('settings.maildriver'),
'host' => config('settings.mailhost'),

But I'm getting this error:

Missing argument 1 for Illuminate\Support\Manager::createDriver()

Update:

I have created a new service provider MailServiceProvider to override the settings in Mail.php like this:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Config;

class MailServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        Config::set('mail.driver', config('settings.maildriver'));
        Config::set('mail.host', config('settings.mailhost'));
        Config::set('mail.port', config('settings.mailport'));
        Config::set('mail.encryption', config('settings.mailencryption'));
        Config::set('mail.username', config('settings.mailusername'));
        Config::set('mail.password', config('settings.mailpassword'));

    }
}

But still I am getting the same error!!

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?

Saurabh
  • 2,655
  • 1
  • 20
  • 47
  • How and where are you `echo`ing these values? – Taylor Foster Jul 17 '17 at 15:01
  • When i echo in my blade files (something.blade.php) using `{{ config('settings.sitename') }}` it works fine. But I am trying to echo it in `Mail.php` file present in `App\config\Mail.php' – Saurabh Jul 17 '17 at 15:10
  • Maybe just stick to the `.env` way of doing it? Ex: `'driver' => env('MAIL_DRIVER', 'smtp')`. – Taylor Foster Jul 17 '17 at 15:18
  • In my Webapp, I want to give the ability to admin to change the Mail Service Provider details from the Admin Dashboard. So, there is a from in the dashboard to get maildriver, mailport, mailusername etc, and are stored in the database. I don't want the admin to change the code manually. – Saurabh Jul 17 '17 at 15:24

4 Answers4

40

Struggled for 3 days with this issue finally I figured out a way to solve it.

First I created a table mails and populated it with my values. Then I created a provider MailConfigServiceProvider.php

<?php

namespace App\Providers;

use Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class MailConfigServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        if (\Schema::hasTable('mails')) {
            $mail = DB::table('mails')->first();
            if ($mail) //checking if table is not empty
            {
                $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 then registered it in the config\app.php

App\Providers\MailConfigServiceProvider::class,
Saurabh
  • 2,655
  • 1
  • 20
  • 47
7

Maybe its usefull to somebody, but I solved it as following;

In a ServiceProvider under the boot-method;

$settings = Cache::remember('settings', 60, function () {
    return Setting::pluck('value', 'name')->all();
});

config()->set('settings', $settings); // optional

config()->set('mail', array_merge(config('mail'), [
    'driver' => 'mailgun',
    'from' => [
        'address' => $settings['mail_from_address'],
        'name' => $settings['mail_from_name']
    ]
]));

config()->set('services', array_merge(config('services'), [
    'mailgun' => [
        'domain' => $settings['mailgun_domain'],
        'secret' => $settings['mailgun_secret']
    ]
]));

I used array_merge with the original config, so we only override the values we need to. Also we can use the Cache-facade in the boot-method.

Ramon Bakker
  • 1,075
  • 11
  • 24
1

Following the instructions here is the proper solution to the problem, in case if you're sending multiple emails per request over different SMTP configurations, Config::set() won't work right, the first email will use the correct settings, while all upcoming emails within the same request will use the same configuration of the first one, because the Mail service is provided as a singleton thus only the initial configurations will be used.

This also might affect emails sent over Laravel queue workers due to the same reason.

Mohyaddin Alaoddin
  • 2,465
  • 1
  • 19
  • 14
  • The link you shared isn't elaborate enough. I'm having difficulties implementing it. Any help? I need exactly what you suggested here – Ikechukwu Aug 21 '20 at 10:43
1

After research a lot, finally I found the best possible way to dynamic mail configuration.

I am saving my mail configuration data in the settings table, please have a look at the table structure.

enter image description here

Helpers/AaapHelper.php

<?php

namespace App\Helpers;

use App\Setting;

class AppHelper
{

    public static function setMailConfig(){

        //Get the data from settings table
        $settings = Setting::pluck('description', 'label'); 

        //Set the data in an array variable from settings table
        $mailConfig = [
            'transport' => 'smtp',
            'host' => $settings['smtp_host'],
            'port' => $settings['smtp_port'],
            'encryption' => $settings['smtp_security'],
            'username' => $settings['smtp_username'],
            'password' => $settings['smtp_password'],
            'timeout' => null
        ];

        //To set configuration values at runtime, pass an array to the config helper
        config(['mail.mailers.smtp' => $mailConfig]);
    }
}

app\Http\Controllers\SettingController.php

<?php
namespace App\Http\Controllers;

use App\Helpers\AppHelper;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;

class SettingController extends Controller
{

    public function sendMail()
    {
        try
        {
            //Set mail configuration
            AppHelper::setMailConfig();

            $data = ['name' => "Virat Gandhi"];

            Mail::send(['text' => 'mail'], $data, function ($message)
            {
                $message->to('abc@gmail.com', 'Lorem Ipsum')
                    ->subject('Laravel Basic Testing Mail');
                $message->from('xyz@gmail.com', $data['name']);
            });
            return redirect()->back()->with('success', 'Test email sent successfully');
        }
        catch(\Exception $e)
        {
            return redirect()->back()->withErrors($e->getMessage());
        }
    }
}

Explanation

While sending a mail through the sendMail function it will first configure mail through helper.

Soubhagya Kumar Barik
  • 1,979
  • 20
  • 26
  • this works fine when the emails are not queueable but when it comes to queueable mails after setting up everything when i try to get credentials they are empty in my listener any idea why is this so? – Yasir Mehmood Jun 30 '22 at 09:26
  • Try to create a job file and put your mail function (Mail::send) inside that. get the credentials in the controller and pass them to the job. – Soubhagya Kumar Barik Jun 30 '22 at 09:41