Laravel: Load a page and then send a mail - php

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.

Related

How to make custom email verification (Laravel 5.8)? [duplicate]

This question already has answers here:
How to customize the email verification email from Laravel 5.7?
(9 answers)
Closed 2 years ago.
I can make email verification using default template from Laravel 5.8.
Now, I want to customize the email.
Can you help me?
I tried to follow instructions from this: https://medium.com/#lordgape/custom-verification-email-notification-laravel-5-8-573ba3183ead. There is no error, but no email sent.
When I switch back to default verification, the default email is sent.
Edit:
The steps I tried so far:
Create a mailable for email verification.
Create views for new email template
override toMailUsing() using the following code in AppServiceProvider.php:
VerifyEmail::toMailUsing(function ($notifiable){
$verifyUrl = URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
['id' => $notifiable->getKey()]
);
// dd($notifiable);
return new EmailVerification($verifyUrl, $notifiable);
});
edit mailable, add two variable: $verifyUrl and $user.
edit __construct function:
public function __construct($url, User $user)
{
$this->user = $user;
$this->verifyUrl = $url;
}
edit build() function in mailable, add return $this->view('emails.verifyUser'); (views of custom template).
No error, "please verify your email" page is shown like usual. But no email sent.
If you want to only customize the look and the layout of the sent email and not the contents (text) of the mail you have to publish the view files of the notification and mail components.
To do so you can type:
php artisan vendor:publish --tag=laravel-notifications
php artisan vendor:publish --tag=laravel-mail
These command will copy the Laravel mail message templates from the vendor folder to resources/views/vendor/notifications and resources/views/vendor/mail.
In the former path you have the email.blade.php which is basically the the MailMessage layout. (In fact you can see all the available slots you can customize with MailMessage).
In the latter path you can find the markdown and html components (that are also used in the MailMessage layout I mentioned earlier). You can take a look at the various files and modify them.
Note: the changes you makes to these templates would apply to any email message that you send, also if you create a new notification and mailable, it would still use these modified templates.
You can use this method to do global changes to the look of an email and, for example customize the header to include the logo of your company, etc.
If you just have to edit the content (texts) of an email message, you should still have to create your custom notification/mailable.
Quick tip: you can preview emails in the browser to make adjustments to the layout and texts quickly, as you don't have to fire off the notification all the time: https://medium.com/#jaouad_45834/preview-your-emails-notifications-in-browser-laravel-9058d8c856c4

Laravel testing on demand notification

I have a slack notification class that sends a message inside our company slack account, in a specific channel, every time an user performs the activation process.
The system works, but it's manually tested and that's not cool.
The notification is sent by a listener attached to an UserHasBeenActivated event, the listener is the following:
public function handle(UserHasBeenActivated $event)
{
Notification::route("slack", config("services.slack.user.url"))
->notify(new UserActivated($event->user));
}
Pretty straight forward. The problem here is that the notification is on demand thus it's difficult to test... because there isn't any sort of documentation on how to test on demand notifications!
At the moment I'm stuck here:
public function it_sends_a_notification_when_an_user_is_activated()
{
Notification::fake();
$user = factory(User::class)->states("deleted")->create();
$user->activate();
Notification::assertSentTo(
$user,
UserActivated::class
);
}
Of course this test fails, the activate() method is what triggers the Event UserHasBeenActivated and sequentially all the listeners, and one of them sends the corresponding notification.
Do you know how to test on demand Notifications? Is there any hidden API that am I missing?
For the newcomers
Laravel introduces in v5.5.14 the ability to test anonymous notification by using the provided Notification::fake() system.
More about this new feature here: https://github.com/laravel/framework/pull/21379

Sending bulk emails using different credentials

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.

Laravel email with queue 550 error (too many emails per second)

Our emails are failing to send using Laravel with a Redis Queue.
The code that triggers the error is this: ->onQueue('emails')
$job = (new SendNewEmail($sender, $recipients))->onQueue('emails');
$job_result = $this->dispatch($job);
In combination with this in the job:
use InteractsWithQueue;
Our error message is:
Feb 09 17:15:57 laravel: message repeated 7947 times: [ production.ERROR: exception 'Swift_TransportException' with message 'Expected response code 354 but got code "550", with message "550 5.7.0 Requested action not taken: too many emails per second "' in /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php:383 Stack trace: #0 /home/laravel/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php(281):
Our error only happens using Sendgrid and not Mailtrap, which spoofs emailing sending. I've talked with Sendgrid and the emails never touched their servers and their service was fully active when my error occurred. So, the error appears to be on my end.
Any thoughts?
Seems like only Mailtrap sends this error, so either open another account or upgrade to a paid plan.
I finally figured out how to set up the entire Laravel app to throttle mail based on a config.
In the boot() function of AppServiceProvider,
$throttleRate = config('mail.throttleToMessagesPerMin');
if ($throttleRate) {
$throttlerPlugin = new \Swift_Plugins_ThrottlerPlugin($throttleRate, \Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE);
Mail::getSwiftMailer()->registerPlugin($throttlerPlugin);
}
In config/mail.php, add this line:
'throttleToMessagesPerMin' => env('MAIL_THROTTLE_TO_MESSAGES_PER_MIN', null), //https://mailtrap.io has a rate limit of 2 emails/sec per inbox, but consider being even more conservative.
In your .env files, add a line like:
MAIL_THROTTLE_TO_MESSAGES_PER_MIN=50
The only problem is that it doesn't seem to affect mail sent via the later() function if QUEUE_DRIVER=sync.
For debugging only!
If you don't expect more then 5 emails and don't have the option to change mailtrap, try:
foreach ($emails as $email) {
...
Mail::send(... $email);
if(env('MAIL_HOST', false) == 'smtp.mailtrap.io'){
sleep(1); //use usleep(500000) for half a second or less
}
}
Using sleep() is a really bad practice. In theory this code should only execute in test environment or in debug mode.
Maybe you should make sure it was really sent via Sendgrid and not mailtrap. Their hard rate limit seems currently to be 3k requests per second against 3 requests per second for mailtrap on free plan :)
I used sleep(5) to wait five seconds before using mailtrap again.
foreach ($this->suscriptores as $suscriptor) {
\Mail::to($suscriptor->email)
->send(new BoletinMail($suscriptor, $sermones, $entradas));
sleep(5);
}
I used the sleep(5) inside a foreach. The foreach traverses all emails stored in the database and the sleep(5) halts the loop for five seconds before continue with the next email.
I achieved this on Laravel v5.8 by setting the Authentication routes manually. The routes are located on the file routes\web.php. Below are the routes that needs to be added to that file:
Auth::routes();
Route::get('email/verify', 'Auth\VerificationController#show')->name('verification.notice');
Route::get('email/verify/{id}', 'Auth\VerificationController#verify')->name('verification.verify');
Route::group(['middleware' => 'throttle:1,1'], function(){
Route::get('email/resend', 'Auth\VerificationController#resend')->name('verification.resend');
});
Explanation:
Do not pass any parameter to the route Auth::routes(); to let configure the authentication routes manually.
Wrap the route email/resend inside a Route::group with the middleware throttle:1,1 (the two numbers represents the max retry attempts and the time in minutes for those max retries)
I also removed a line of code in the file app\Http\Controllers\Auth\VerificationController.php in the __construct function.
I removed this:
$this->middleware('throttle:6,1')->only('verify', 'resend');
You need to rate limit emails queue.
The "official" way is to setup Redis queue driver. But it's hard and time consuming.
So I wrote custom queue worker mxl/laravel-queue-rate-limit that uses Illuminate\Cache\RateLimiter to rate limit job execution (the same one that used internally by Laravel to rate limit HTTP requests).
In config/queue.php specify rate limit for emails queue (for example 2 emails per second):
'rateLimit' => [
'emails' => [
'allows' => 2,
'every' => 1
]
]
And run worker for this queue:
$ php artisan queue:work --queue emails
I had this problem when working with mail trap. I was sending 10 mails in one second and it was treated as spam. I had to make delay between each job. Take look at solution (its Laravel - I have system_jobs table and use queue=database)
Make a static function to check last job time, then add to it self::DELAY_IN_SECONDS - how many seconds you want to have between jobs:
public static function addSecondsToQueue() {
$job = SystemJobs::orderBy('available_at', 'desc')->first();
if($job) {
$now = Carbon::now()->timestamp;
$jobTimestamp = $job->available_at + self::DELAY_IN_SECONDS;
$result = $jobTimestamp - $now;
return $result;
} else {
return 0;
}
}
Then use it to make sending messages with delay (taking in to consideration last job in queue)
Mail::to($mail)->later(SystemJobs::addSecondsToQueue(), new SendMailable($params));

Disable Laravel email during db seed

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.

Categories