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:
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?
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,
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.
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.
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.
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.
Related
I'm having some trouble with sending email using laravel. I've looked around the stackoverflow for solutions but none worked so far. Here's my env and code so far.
MAIL_DRIVER=smtp
MAIL_HOST=smtp.googlemail.com
MAIL_PORT=465
MAIL_USERNAME=myemail.gmail.com
MAIL_PASSWORD=mypassword
MAIL_ENCRYPTION=ssl
MAIL_SETUP=false
MAIL_FROM_ADDRESS=myemail.gmail.com
MAIL_FROM_NAME="Namehere"
and this is my mail.php file
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'smtp.googlemail.com'),
'port' => env('MAIL_PORT', 465),
'encryption' => env('MAIL_ENCRYPTION', 'ssl'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
],
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'myemail#gmail.com'),
'name' => env('MAIL_FROM_NAME', 'myemail#gmail.com'),
],
Now this is my code. It's just a simple to test the email function. This is the class created in mail folder
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class NewTicketMail extends Mailable
{
use Queueable, SerializesModels;
public $details;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($details)
{
$this->$details;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->subject('Mail from ')->view('emails.new_ticket_mail');
}
}
and this is the route called
Route::get('send-email', function(){
$details = [
'title'=> 'New Ticket Received',
'body' => 'We have received your ticket and will process it. Please do not reply to this
email'
];
\Mail::to('receiver#gmail.com')->send(new \App\Mail\NewTicketMail($details));
return view('emails.thanks');
});
the route works if i commented out the Mail to line. any suggestions? I've been at it for hours.
In your mail class you are accessing a properties property which does not exist.
Rather in your mailable constructor do something like
public function __construct($details){
$this->details = $details;
}
This is for sure one issue. As laravel will throw an unknown property error.
P.s. make sure your env has debug as true if you are not seeing errors in your logs.
I´m traying to use my new serviceProvider into mail.php because i need get values from database not .env i can show one solution in this site that one person creaed a serviceProvider and get all data that he needed.
My question is, how i can use this provider into mail.php?
my provider:
public function register()
{
if (\Schema::hasTable('app_settings')) {
$mail = DB::table('app_settings')->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);
}
}
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
mail.php
for example: 'driver' => env('MAIL_DRIVER', 'smtp'),
how i can call driver of provider into driver mail.php
thanks for help, rewards
You can't do it, and it's not a good idea.
The simplest way is to override your config after everything was loaded.
You can use your AppServiceProdiver.php file, and in the boot() function :
public function boot()
{
// adapt of course...
$yourConfig = \DB::select('SELECT * FROM your_config_table');
config()->set([
'app.mail.driver' => $yourConfig[0]->value
]);
}
I am trying to send emails with mailgun but they won't send and I have no idea why because i don't get any errors at all.
This is my code:
mail.php:
'driver' => env('MAIL_DRIVER', 'mailgun'),
services.php:
'mailgun' => [
'domain' => env('sandbox1e...60.mailgun.org'),
'secret' => env('key-146...419'),
],
EmailController.php:
public function send($email, $uuid = null)
{
if($uuid == null){
$uuid = User::get()->where('customer_email' , $email)->first()->email_confirmed;
}
return Mail::to($email)->send(new ConfirmEmail($uuid));
}
ConfirmEmail.php:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class ConfirmEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $uuid;
public function __construct($uuid)
{
$this->uuid = $uuid;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('mailgun#sandbox1e17506823f2490ba9cc78cbbc2adb60.mailgun.org')
->view('emails.confirm');
}
}
I have added the emailadress I want to send to in mailgun, but it's not working. Am I doing something wrong or is there any way I can debug this?
Your configuration is wrong:
'mailgun' => [
'domain' => env('sandbox1e...60.mailgun.org'),
'secret' => env('key-146...419'),
],
The env function looks for an environment variable with the name you provide and returns the value. You should change it to the name of an environment variable and define it in your .env or don't use the env function, but that's not recommended.
Whereas Esteban Garcia's answer is correct, I to wish improve it with code snippets showing how exactly the configuration should look like:
In your config/services.php, leave the configuration as shown below:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
In your .env file, that is where you define the actual mailgun credentials:
MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=sandbox1e...60.mailgun.org
MAILGUN_SECRET=key-146...419
In your laravel .env configure these things
MAIL_DRIVER=mailgun
MAIL_USERNAME=postmaster#sandboxXXXXXX.mailgun.org
MAILGUN_DOMAIN=sandboxXXXXX.mailgun.org
MAILGUN_SECRET=XXXXXXX
After that goto vendor->guzzlehttp->src->client.php find
configureDefaults() method have array name called defaults that array
have verify => true change to false
'verify' => false,
then configure mail.php
'from' => ['address' => 'youremail#gmail.com', 'name' => 'yourname'],
go to route.php and test email is working or not send simple email.
plz make sure you install laravel mail class or install using this
command composer require guzzlehttp/guzzle
use Illuminate\Support\Facades\Mail;
Route::get('/', function () {
$data = [
'title'=>'title here',
'Content'=>'simple content'
];
Mail::send('email.test',$data, function ($message){
$message->to('youremail#gmail.com', 'John Smith')->subject('Welcome!');
});
});
So I definitely can't wrap my head around this one. I'm following a Laravel 5.2 tutorial here.
http://blog.damirmiladinov.com/laravel/laravel-5.2-socialite-facebook-login.html#.V2gUIrgrJPY
And getting the error listed above in the title. My routes look like this:
Route::get('/', function () {
if(Auth::check()) return view('auth/register');
return view('auth/login');
});
Route::get('/redirect', 'MailAuthController#redirect');
Route::get('/callback', 'MailAuthController#callback');
Controller looks like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Socialite;
class MailAuthController extends Controller
{
//
public function redirect()
{
return \Socialite::with('microsoft')->redirect();
}
public function callback()
{
// when microsoft calls with token
}
public function user()
{
}
}
And services.php looks like this:
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Stripe, Mailgun, Mandrill, and others. This file provides a sane
| default location for this type of information, allowing packages
| to have a conventional place to find your various credentials.
|
*/
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
'mandrill' => [
'secret' => env('MANDRILL_SECRET'),
],
'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'region' => 'us-east-1',
],
'sparkpost' => [
'secret' => env('SPARKPOST_SECRET'),
],
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
'microsoft' => [
'client_id' => env('MICROSOFT_CLIENT_ID'),
'client_secret' => env('MICROSOFT_CLIENT_SECRET'),
'redirect' => env('http://localhost:8000/callback'),
],
];
And other than that I have no idea where I might be going wrong. Light my way!
I would recommend using the Microsoft Graph provider from the Socialite Providers package.
Pull in the Microsoft-Graph provider via your composer.json file:
"require": {
...
"laravel/socialite": "^2.0",
"socialiteproviders/microsoft-graph": "dev-master"
},
Run composer update.
Next, add the connection credentials to config/services.php:
...
'graph' => [
'client_id' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxx',
'redirect' => 'https://my-app.dev',
],
*Note: if committing config/services.php to a public repo, extract these values to your .env file and reference them via the env helper method;
In config/app.php add the SocialiteProviders/Generators service provider to the providers array:
'providers' => [
...
/*
* Package Service Providers...
*/
Laravel\Socialite\SocialiteServiceProvider::class,
// This is a dependency of the socialiteproviders/microsoft-graph provider, and will be installed with the provider via it's composer.json file
SocialiteProviders\Manager\ServiceProvider::class,
Register the Socialize facade (also in config/app.php):
'aliases' => [
...
'Socialize' => 'Laravel\Socialite\Facades\Socialite',
],
Register an event listener in app/Providers/EventServiceProvider.php:
protected $listen = [
...
'SocialiteProviders\Manager\SocialiteWasCalled' => [
'SocialiteProviders\Graph\GraphExtendSocialite#handle'
],
];
Create your controller to handle the requests:
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Socialize;
class AuthController extends \App\Http\Controllers\Controller
{
/**
* Redirect the user to the Graph authentication page.
*
* #return Response
*/
public function redirectToProvider()
{
return Socialize::with('graph')->redirect();
}
/**
* Obtain the user information from graph.
*
* #return Response
*/
public function handleProviderCallback(Request $request)
{
$user = Socialize::with('graph')->user();
// $user->token;
}
}
Finally add your routes in routes/web.php:
<?php
Route::get('auth/graph', 'Auth\AuthController#redirectToProvider');
Route::get('auth/graph/callback','Auth\AuthController#handleProviderCallback');
If anyone still arrives here with the same error, but using the SocialiteProviders Microsoft provider already:
Check if you have set up the library correctly.
Make sure to install socialiteproviders/microsoft from composer
Add the SocialiteProviders Manager to your config/providers.php: \SocialiteProviders\Manager\ServiceProvider::class
Add the event listener to your app/Providers/EventServiceProvider.php:
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
[SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class, 'handle'],
],
The last step is important, and what caused the error for me, because I didn't understand the event listener is required (and not just an optional way to extend the provider).
So this might seem obvious but both in my case and judging from the info provided in the question, this step is also missing. I changed from the current Microsoft to the Graph and still got the same error, however I then realized this error happens when the Driver is not registered in the service provider. Make sure you are using the same spelling of the service provider in vendor and that you include the Service provider, in my case:
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* #var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
// ... other providers
/*--- I forgot this --->*/\SocialiteProviders\Graph\GraphExtendSocialite::class.'#handle',
],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*
* #return bool
*/
public function shouldDiscoverEvents()
{
return false;
}
}
This added worked with microsoft graph, driver: "graph" from: https://github.com/SocialiteProviders/Microsoft-Graph
I never got to try with the listed driver "Microsoft" on socialiteproviders.com
and as of the time of this writing Graph was removed from that website, however all I care is that it works and it worked as expected!
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?
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
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);
});
}
}
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
];
}
}
}
And finally, I replaced 'Illuminate\Mail\MailServiceProvider', in config/app.php with 'App\Providers\CustomMailServiceProvider',
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
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);
});
}
#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);
});
}