How to use serviceProvider into other file laravel - php

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
]);
}

Related

Laravel 7 internal server error 500 when sending email

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.

Setting dynamic database in config during Login - Laravel

I'm trying to change the database connection on login based on the user's company.
Here my user has a company and its DB is companyA.
Below is my LoginController where I changed the connection:
public function authenticated(Request $request,User $user)
{
\Config::set('database.connections.dynamicdb', array(
'driver' => 'mysql',
'host' => '127.0.0.1',
'database' => $user->company,
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'strict' => false,
'options' => [
\PDO::ATTR_EMULATE_PREPARES => true
]
));
return redirect()->intended($this->redirectPath());
}
So based on user->company which is already defined in users table, the database name is changed.
But somehow it doesn't work. The error shown is
No database selected.
I tried below code to check if the values are set during login.
return \Config::get('database.connections.dynamicdb');
It showed all values set to my requirements. But when I check after login and reaching /home, the value of database in config is null.
So what all changes should I do. Is my technique right? Or is there any other solution for this.
In my Stock Model i have added the below lines:
protected $table = 'stocks';
protected $connection = 'dynamicdb';
And the query I'm running is just a get all query:
Stock::orderBy('tag_no','asc')->get()
Can anyone please tell me why this happens? What should i do?
All requests are stateless so current request doesn't know that you set new config value in previous request.
You should call Config::set(...) every time when you want to use dynamic database and set database name getting this value from User instance.
Setting above should be done using middleware and service provider.
Create new middleware and register it for web middleware group (You may do this using the $middlewareGroups property of your HTTP kernel):
protected $middlewareGroups = [
'web' => [
//...
\App\Http\Middleware\YourMiddleware::class,
],
//...
];
Then:
<?php namespace App\Http\Middleware;
class YourMiddleware
{
public function handle($request, Closure $next)
{
if (Auth::check()) {
$database_name = Auth::user()->company;
// Set your config here using $user->company
// ...
}
return $next($request);
}
}
If you must to set this value once (during authentication), you should combine above code and sessions to store this information between requests:
session(['db_name' => $dbname]); // Set db_name and store it
$db_name = session('db_name'); // Get db_name from session
Read more about HTTP Sessions: https://laravel.com/docs/5.7/session#retrieving-data
First you need create new default db for connection and add to database.php like normal connection
'dynamicdb' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => 'default',
//others
],
next overriding model methods in Stock
protected $table = 'stocks';
protected $connection = 'dynamicdb';
/**
* #return string
*/
public function getTable()
{
$table = parent::getTable();
$database = config('database.connections.dynamicdb.database');
return starts_with($table, $database)
? $table
: $database . '.' . parent::getTable();
}
/**
* Set the table associated with the model.
*
* #param string $table
* #return $this
*/
public function setTable($table)
{
$database = config('database.connections.dynamicdb.database');
$this->table = starts_with($table, $database)
? $table
: $database . '.' . $table;
return $this;
}
Usage : \Config::set('database.connections.dynamicdb.database',$user->company); or you can create helper for it
Don't forget this method work only one connection and connected user have access all databases
Add Multiple DB in .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database1
DB_USERNAME=root
DB_PASSWORD=
ALT_DB_HOST=127.0.0.1
ALT_DB_PORT=3306
ALT_DB_DATABASE=database2
ALT_DB_USERNAME=root
ALT_DB_PASSWORD=
Edit config/database.php
'connections' => [
'mysql' => [
......
],
'alt_mysql' => [
'driver' => 'mysql',
'host' => env('ALT_DB_HOST', '127.0.0.1'),
'port' => env('ALT_DB_PORT', '3306'),
'database' => env('ALT_DB_DATABASE', 'vibecloud'),
...
],
If Whole model used for ALT_MYSQL then
protected $connection = 'alt_mysql';
ELSE
protected function stock_info() {
return \DB::connection('alt_mysql')->select('*')->from('stocks')->get();
}

Dynamic mail configuration with values from database [Laravel]

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.

Laravel Mail::send returns zero with no specific error in Mail::failures()

I am using smtp driver and this is my code to send email in laravel 5.2:
public function Sendmail()
{
$data["mail_message"] = "Hello!";
if(Mail::send('Emails.email', $data, function($message)
{
$message->from('webmaster#example.com', Input::get('name'));
$message->to('amirhasan.hesam#gmail.com')->subject('Welcome to My Laravel app!');
}))
{
return "success";
}
else
{
return Mail::failures();
}
}
the Mail::failures() returns ["amirhasan.hesam#gmail.com"] with no specific error!
and this is my config on mail.php :
return [
'driver' => env('MAIL_DRIVER', 'smtp'),
'host' => env('MAIL_HOST', '*******'),
'port' => env('MAIL_PORT', 587),
'from' => ['address' => "****#*****", 'name' => "Diling"],
'encryption' => env('MAIL_ENCRYPTION', ''),
'username' => env('*****#*****'),
'password' => env('*************************'),
'sendmail' => '/usr/sbin/sendmail -bs',
'pretend' => false,
];
and I am using xamp right now to test the email. Any thoughts?
I've had troubles with using variables inside the mail::send .. and im also not sure if mail::send returns boolean or such... I've used something like what I wrote down in the past.
$nameSend = Input::get('name');
Mail::send('Emails.email', $data, function($message) use ($nameSend){
$message->from('webmaster#example.com', $nameSend);
$message->to('amirhasan.hesam#gmail.com')->subject('Welcome to My Laravel app!');
});
.
.
if( count(Mail::failures()) > 0 ) {
$output = "There was one or more failures. They were: \n";
foreach(Mail::failures as $email_address) {
$output = $output. $email_address ."\n";
}
return $output;
}
return "Success!";
you just need to use Mail facade
use Illuminate\Support\Facades\Mail;

How to use database for mail settings in Laravel

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);
});
}

Categories