I need to send hundreds of emails using different credentials from laravel.
Each customer of mine has his/hers mail list and needs to provide their own SMTP server. I process that list and send emails on customer's behalf.
This is what I have so far. It is working, but it is very slow and I don't have many emails so far. I see a problem when I get more emails.
Any suggestions on how to improve?
PS- I use cron Console Command and use Kernel to schedule the job.
public function sendMailings($allMailings) {
foreach ($allMailings as $email) {
Config::set('mail.host', $email['smtpServer']);
Config::set('mail.port', $email['smtpPort']);
Config::set('mail.username', $email['smtpUser']);
Config::set('mail.password', $email['smtpPassword']);
Config::set('mail.encryption', $email['smtpProtocol']);
Config::set('mail.frommmail', trim($email['fromEmail']));
Config::set('mail.fromuser', trim($email['fromUser']));
Config::set('mail.subject', trim($email['subject']));
Config::set('mail.toEmail', trim($email['toEmail']));
Config::set('mail.toName', trim($email['toName']));
Config::set('mail.pretend', false);
$email_body = $email['emailBody'];
Mail::send('emails.availability, compact('email_body')
, function($message) {
$message->from(config('mail.username'), config('mail.fromUser'));
$message->replyTo(config('mail.frommmail'), config('mail.fromUser'));
$message->to(config('mail.toEmail'), config('mail.toName'))->subject(config('mail.subject'));
});
Log::info('Mail was sent');
}
}
You can not change email provider configs on-the-fly, so you must make new instance of mailer in service container. I did it before, i wrote a method in my own class to get new mailer instance:
/**
* #return Mailer
*/
protected function getMailer()
{
// Changing mailer configuration
config(['mail.driver' => static::getName()]);
// Register new instance of mailer on-the-fly
(new MailServiceProvider($this->container))->register();
// Get mailer instance from service container
return $this->container->make('mailer');
}
Sending e-mail messages directly in web app can drastically slow down the responsiveness of your application. You should always queue your messages.
Instead of Mail::send You can use Mail::queue
and then from cron or manually call
php artisan queue:work
That will process the next item on the queue. This command will do nothing if the queue is empty. But if there’s an item on the queue it will fetch the item and attempt to execute it.
Related
I need to modify the mail SMTP parameters such as MAIL_HOST and MAIL_USERNAME dynamically.
For, this I am using Config::set() to set these values dynamically.
# This code works
Config::set('mail.host', 'smtp.gmail.com');
Mail::to('user#example.com')->send(new myMailable());
The above code works if I do not queue the mail.
The moment I queue it, it appears that Config::set() fails to set the values.
Test to confirm Config::set() not working with queued jobs -
I created a simple job and put the below code in the handler.
public function handle()
{
# set the config
Config::set('mail.host', 'smtp.gmail.com');
# confirm config has been set correctly
logger('Setting host to = [' . config('mail.host') . ']');
}
The above code creates the below log entry.
Setting host to = []
Why can I not change the Config on-the-fly for queued jobs? And how to solve this?
This is because the Queue worker doesn't use the current request. It is a stand-alone process, not being interferred by config settings.
To let this work, you need to use a Job. The dispatch function takes your data and sends it through to the job itself. From your controller, call:
JobName::dispatch($user, $settings);
In the job you set the variables accordingly:
public function __construct($user, $settings)
{
$this->user = $user;
$this->settings = $settings;
}
Then in the handle method:
\Notification::sendNow($this->user, new Notification($this->settings));
You can use a normal notification for this.
Do not for get to add implements ShouldQueue to your job!
In my project I'm trying to send e-mail through my own SMTP server using Laravel 5. I have everything set up correctly using the SMTP driver and I'm managing to send and receive e-mail fine.
I want to get set up using DKIM. I've set up my public key and made it available in my DNS and I have a private key ready to start signing my messages.
However, I can't find any documentation on how to set up DKIM signing using Laravel/Swift Mailer. I've managed to sign my e-mails with DKIM before in another non-Laravel project that used PHPMailer but can't find any way of doing it here. I've had a browse through Illuminate\Mail\Message and Illuminte\Mail\Mailer but can't find anything relevant.
Does anyone know how to do this?
Current example code:
public function handle(UserWasRegistered $event)
{
$user = $event->getUser();
$this->mailer->send(['emails.users.welcome.html', 'emails.users.welcome.text'], ['user' => $user], function($message) use($user) {
$message->subject('Welcome to XXXXXX');
$message->to($user->email);
});
}
Ideally, I would like to be able to provide my DKIM private key in the config somewhere and have Laravel/Swift Mailer (Or write some code once) sign my messages for me.
Cheers
I wrote decorator of MailServiceProvider for Laravel 5 that provides ability to sign outgoing messages with DKIM:
https://github.com/vitalybaev/laravel5-dkim
You have to extend Laravel Mailer and mail service provider. Default laravel mailer using new Swift_Message class not Swift_SignedMessage thats why you don't have options for signing.
Here is a package for laravel 4.
i am trying to load page first and then activate the $mailer to send email. because when i click on to go to next page its taking time, because its sending emails and then it is loading so,
what is the best way to do it. or any way. because i cant figure it out.
here is snippet
public function sInterest($project_id, AppMailer $mailer)
{
$project = Project::findOrFail($project_id);
if($project->investment){
$mailer->sendInterestNotificationI($user, $project);
$mailer->sendInterestNotificationD($project, $user);
$mailer->sendInterestNotificationA($project, $user);
return view('projects.offer', compact('project'));
}
}
is there a way $mailer to activate after returning a page?
In your AppMailer sendInterestNotification*() methods, replace sync email delivery with queued email delivery (see Queueing Mail in Laravel documentation)
Then page will be returned instantly, emails will be put in the corresponding queue. You will have to edit .env file to change the QUEUE driver and to start a queue listener as a separate process, detailed documentation is given on Laravel website
There is no way you can return a page to browser and then run some extra commands in your controller.
I had change pretend as true in mail config. please check http://laravel.com/docs/4.2/mail#mail-and-local-development
Now log is showing to address like this.
[2014-11-22 17:12:49] production.INFO: Pretending to mail message to: dinukathilanga#gmail.com, bbelekkaya#gmail.com [] []
But i need debug cc emails also. How can i do it?
This is my code.
Mail::send($view, $data, function($message) use ($to, $cc, $subject)
{
foreach ($to as $toUser) {
$message->to($toUser['email'], $toUser['first_name'] . ' ' . $toUser['last_name']);
}
foreach ($cc as $ccUser) {
$message->cc($ccUser['email'], $ccUser['first_name'] . ' ' . $ccUser['last_name']);
}
$message->subject($subject);
});
Allow me to be frank here. I am new to Laravel, but I will try my best to explain this.
I do know how to debug email platforms though, the easiest process is by doing it through C.
I will try to be succint as possible.
Firstly, try using the laravel-debugbar (the latest version is 4).
This is an application that is capable of filtering the PHP Debug Bar.
By using this application you will be able to attach and edit output functions (here is the link for more information: Debug bar.
If this won't work, try debugging through C.
Fristly, you will compile the C program by inserting the debug option, option -g.
Afterwards, you'll launch the gdb into the platform.
Thirdly, you'll break the point in the C program, specifically, insert the break line_number function.
After that, you'll print the variable values in the gdp debugger.
For instance, you'll type commands such as print j and (gdp) p i (I will post the website where I've got this information from; it will give you a more broader walkthrough).
There are various operation for this process.
I strongly advise you visiting:Debug C program using gdb. Hope this helped.
Create a new class named ExtendedMailer for the sake of this example and save the file somewhere the autoloader is able to find it. Depending on where you put the file, you may need to run composer dump-autoload once you've saved it.
<?php
use Illuminate\Mail\Mailer;
class ExtendedMailer extends Mailer
{
protected function logMessage($message)
{
parent::logMessage($message);
$emails = implode(', ', array_keys((array) $message->getCc()));
$this->logger->info("Pretending to mail message to: {$emails}");
}
}
Create a new service provider, somewhere your application is able to load classes. As above, you may need to run composer dump-autoload
The below code simply extends the original MailServiceProvider but allows us to bind a different class to in the IoC, you'll notice the new ExtendedMailer; the class we created earlier. Obviously if you namespaced the class, reflect that change here.
<?php
use Illuminate\Mail\MailServiceProvider;
class ExtendedMailServiceProvider extends MailServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$me = $this;
$this->app->bindShared('mailer', function($app) use ($me)
{
$me->registerSwiftMailer();
// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new ExtendedMailer(
$app['view'], $app['swift.mailer'], $app['events']
);
$this->setMailerDependencies($mailer, $app);
// If a "from" address is set, we will set it on the mailer so that all mail
// messages sent by the applications will utilize the same "from" address
// on each one, which makes the developer's life a lot more convenient.
$from = $app['config']['mail.from'];
if (is_array($from) && isset($from['address']))
{
$mailer->alwaysFrom($from['address'], $from['name']);
}
// Here we will determine if the mailer should be in "pretend" mode for this
// environment, which will simply write out e-mail to the logs instead of
// sending it over the web, which is useful for local dev environments.
$pretend = $app['config']->get('mail.pretend', false);
$mailer->pretend($pretend);
return $mailer;
});
}
}
In your config/app.php, you'll find a line which looks like
'Illuminate\Mail\MailServiceProvider',
You'll need to comment it out and add a line as below
'ExtendedMailServiceProvider',
What this does is replace the mailer which Laravel knows about with the one you've just created. The one you've just created is the same as the default one as it merely extends it, and adds functionality to the logMessage function.
I use Mandrill mail driver for tests. I have a remote staging, that I seed after deploy. And during seeding I try to disable email sends, that are linked to certain events.
Placing this in seeder:
Config::set('mail.driver', 'log');
Config::set('mail.pretend', true);
Has no effect. I don't understand why. I place this in root DatabaseSeeder#run or/and in child seeders — the same. Calls to Mandrill are still performed.
Is there a solution for this problem?
The reason your
Config::set('mail.driver', 'log');
Config::set('mail.pretend', true);
aren't working is because the mail object doesn't check these values before sending mail. Whaaaaaaaa?. If you take a look at the sendSwiftMessage method in the mailer class
#File: vendor/laravel/framework/src/Illuminate/Mail/Mailer.php
protected function sendSwiftMessage($message)
{
if ($this->events)
{
$this->events->fire('mailer.sending', array($message));
}
if ( ! $this->pretending)
{
$this->swift->send($message, $this->failedRecipients);
}
elseif (isset($this->logger))
{
$this->logMessage($message);
}
}
You can see the class checks $this->pretending, and not the configuration, before deciding if it should send the mail or not. So what sets pretending? That's in the MailServiceProvider class's register method.
public function register()
{
//...
$pretend = $app['config']->get('mail.pretend', false);
$mailer->pretend($pretend);
//...
}
When Laravel boots up and registers each service provider, it eventually registers the mail service provider and that's when it reads the configuration, and then tells the mailer if it should "pretend" or not. By the time you're calling this in your seeder, the mailer's already loaded it's configuration value.
Fortunately, there's a pretty easy solution. The mailer object is a singleton/shared service, and has public methods available to control if it should pretend or not. Just call the pretend method yourself instead of setting configuration values
Mail::pretend(true); //using the `Mail` facade to access the mailer object.
you should be able to turn the mailer off programatically.
This is an answer for Laravel 5.7, because pretend doesn't exists:
If you want to disable mail while seeding the database, you could simply 'abuse'
Mail::fake()
I think in two possibilities, you can try:
You can set the command to enable the mail pretend on-the-fly:
Mail::pretend();
The db seed are running with more than one request:
As is write here:
Configuration values that are set at run-time are only set for the
current request, and will not be carried over to subsequent requests.
So you can try set this config over requests, like a session, than finish in the end of the seeding.