How to use database for mail settings in Laravel

2019-03-16 20:19发布

问题:

I'd like to keep users away from editing configuration files, so I've made web interface in admin panel for setting up Mail server, username, password, port, encryption.. I was working well in Laravel 4.2, but now when the app has been rewritten into Laravel 5, an error occurs:

Class 'Settings' not found in <b>F:\htdocs\app\config\mail.php</b> on line <b>18</b><br />

For this purpose I've created a service provider, made a facade, put them in config/app.php, Settings::get('var')/Settings::set('var') work perfectly, but not for mail settings.


config/mail.php:

<?php return array(
            'driver' => Settings::get('mail_driver'),
            'host' => Settings::get('mail_host'),
            'port' => Settings::get('mail_port'),
            'from' => array('address' => Settings::get('mail_from_address'), 'name' => Settings::get('mail_from_name')),
            'encryption' => Settings::get('mail_encryption'),
            'username' => Settings::get('mail_username'),
            'password' => Settings::get('mail_password'),
            'sendmail' => Settings::get('mail_sendmail'),
            'pretend' => false,
            );

config/app.php:

'providers' => [

    ...

    'App\Providers\SettingsServiceProvider',

    ...

'aliases' => [

    ...

    'Settings' => 'App\Custom\Facades\Settings',

<?php namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Custom\Settings;

class SettingsServiceProvider extends ServiceProvider {

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

/**
 * Register the application services.
 *
 * @return void
 */
public function register()
{
    $this->app->singleton('settings', function()
    {
        return new Settings;
    });
}

}

<?php namespace App\Custom;

use App\Setting;

class Settings {
public function get($var) {

    try{
        $setting = Setting::first();                

    } catch(exception $e)
    {
        return false;
    }

    return $setting->$var;

}

public function set($var, $val) {

    try{
        $setting = Setting::first();
        $setting->$var = $val;              
        $setting->save();               

    } catch(exception $e)
    {
        return false;
    }

    return true;

}   
}

<?php

namespace App\Custom\Facades;

use Illuminate\Support\Facades\Facade;

class Settings extends Facade {

    protected static function getFacadeAccessor() { return 'settings'; }

}

Any ideas how to implement Laravel mail settings using database?

回答1:

To archive this I created CustomMailServiceProvider by extending Illuminate\Mail\MailServiceProvider so as to overwrite this method:

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

Here is the complete solution

  1. I created CustomMailServiceProvider.php in app\Providers

namespace App\Providers;

use Illuminate\Mail\MailServiceProvider;
use App\Customs\CustomTransportManager;

class CustomMailServiceProvider extends MailServiceProvider{

    protected function registerSwiftTransport(){
        $this->app['swift.transport'] = $this->app->share(function($app)
        {
            return new CustomTransportManager($app);
        });
    }
}
  1. I created CustomTransportManager.php in app/customs directory - NB: app/customs directory doesn't exist in default laravel 5 directory structure, I created it

namespace App\Customs;

use Illuminate\Mail\TransportManager;
use App\Models\Setting; //my models are located in app\models

class CustomTransportManager extends TransportManager {

    /**
     * Create a new manager instance.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return void
     */
    public function __construct($app)
    {
        $this->app = $app;

        if( $settings = Setting::all() ){

            $this->app['config']['mail'] = [
                'driver'        => $settings->mail_driver,
                'host'          => $settings->mail_host,
                'port'          => $settings->mail_port,
                'from'          => [
                'address'   => $settings->mail_from_address,
                'name'      => $settings->mail_from_name
                ],
                'encryption'    => $settings->mail_encryption,
                'username'      => $settings->mail_username,
                'password'      => $settings->mail_password,
                'sendmail'      => $settings->mail_sendmail,
                'pretend'       => $settings->mail_pretend
           ];
       }

    }
}
  1. And finally, I replaced 'Illuminate\Mail\MailServiceProvider', in config/app.php with 'App\Providers\CustomMailServiceProvider',


回答2:

I have added

$this->app['config']['services'] = [
        'mailgun' => [
            'domain' => $settings->mailgun_domain,
            'secret' => $settings->mailgun_secret,
        ]
        ];   

to CustomTransportManager __construct() to include mailgun API credentials that I'm using as mailing service



回答3:

I configured as mentioned, however got the following error. While I tried your code found that from Laravel 5.4 share method is deprecated and instead informed to use singleton.

Call to undefined method Illuminate\Foundation\Application::share()

here is the below method using singleton instead using share method:

protected function registerSwiftTransport(){
    $this->app->singleton('swift.transport', function ($app){
        return new CustomTransportManager($app);
    });
}


回答4:

@DigitLimit , method share() has been dropped since Laravel 5.4. I had to work-around this problem using other methods, and I am not sure they are perfect. Here is my registerSwiftTransport() method in CustomMailServiceProvider class.

Firstly, we need to determine if code is not executed while calling app through command line: "if(strpos(php_sapi_name(), 'cli') === false)". If we don't check that and don't prevent setting new params in this case, Artisan will throw us errors in command line. Secondly, we need to get settings from database somehow. I did it using my method getSettingValue(), where first argument is setting key, and second argument is default value if setting is not found. As you see, I assigned settings to $this->app['config']['mail']. After that, I used singleton() method:

protected function registerSwiftTransport(){
    if (strpos(php_sapi_name(), 'cli') === false) {
       $this->app['config']['mail'] = [
        'driver'        => Setting::getSettingValue('mail_driver', '****'),
        'host'          => Setting::getSettingValue('mail_host', '****'),
        'port'          => Setting::getSettingValue('mail_port', 25),
        'from'          => [
            'address'   => Setting::getSettingValue('mail_from_address', '****'),
            'name'      => Setting::getSettingValue('mail_from_name', '****'),
        ],
        'encryption'    => Setting::getSettingValue('mail_encryption', '***'),
        'username'      => Setting::getSettingValue('mail_username', '****'),
        'password'      => Setting::getSettingValue('mail_password', '****'),
     ];
   }

   $this->app->singleton('swift.transport', function ($app) { 
       return new Illuminate\Mail\TransportManager($app);
   });
}