Send email in background : Laravel 5.4 - laravel

I am using an inbuilt code in Laravel to send Email Notification. Code is below. I am using smtp to send email
class RegisterNotification extends Notification
{
use Queueable;
public function __construct($token)
{
$this->token = $token;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('hi');
}
public function toArray($notifiable)
{
return [
//
];
}
}
Here the problem is, it takes around 5 seconds to complete the process and control does not come back. I am assuming that if it come back and do the email sending work in background...it would save a lot of time.
Is there any inbuilt work to do the same? I meant, control should come back and should say email sent...and then it should do the work in background.
Email Sending code in Controller
class apiRegisterController extends Controller
{
public function Register(RegisterRequest $request) {
$RegisterNotification = new RegisterNotification($Token);
$User->notify($RegisterNotification);
}
}
Code for Queue
Controller Code
$job = (new SendForgotPasswordEmail($Data))->onConnection('database');
dispatch($job);
Job
class SendForgotPasswordEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $Data;
public $tries = 5;
public function __construct($Data)
{
$this->Data = $Data;
}
public function handle()
{
$Token = $this->Data["Token"];
$User = $this->Data["User"];
$RegisterNotification = new RegisterNotification($Token);
$User->notify($RegisterNotification);
}
}

Step 1: Change class RegisterNotification extends Notification to class RegisterNotification extends Notification implements ShouldQueue
Step 2: Implement a queue driver. In your config/queue.php make sure your driver is not set to sync like so: 'default' => env('QUEUE_DRIVER', 'sync'), and make sure your .env doesnt have QUEUE_DRIVER=sync. You can look at the Laravel documentation for queues to choose an appropriate queue driver

You can use the build-in API.
$user = User::findOrFail($id);
Mail::queue('emails.welcome', $data, function ($message) use ($user){
$message->from('hello#app.com', 'Your Application');
$message->to($user->email, $user->name)->subject('Your Reminder!');
});
But first you have to configure the queues.
Add in your .env file the line QUEUE_DRIVER=sync and then write on the terminal php artisan queue:listen.
If you want the queue to run forever on the server use Supervisor. Queues documentation explains how you can use it.

you can use laravel job queue https://laravel.com/docs/5.4/queues
Mail::queue(

Related

Laravel error when sending email notification

Laravel Version: 8.78.1
PHP Version: 8.0.10
I've created a custom command to run on a schedule and email a notification.
My Command class handle method:
public function handle()
{
$sql = "SELECT * FROM Licences WHERE (Expired = 1)";
$list = DB::select($sql);
return (new NotifyExpiredLicences($list))->toMail('me#gmail.com');
}
My notification method:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Clients with Expired Licences')
->markdown('vendor/notifications/expiredlicences',
['clients' => $this->list, 'toname' => 'Me']);
}
Whenever I test this by running it manually with php artisan email:expired-licences I get the following error Object of class Illuminate\Notifications\Messages\MailMessage could not be converted to int from my command class in the handle method.
However, the preview of my email works fine & displays as expected:
Route::get('/notification', function () {
return (new SendExpiredLicences())->handle();
});
If I remove the return statement from my handle() method, then although I get no errors, neither in my console or in storage\logs, also the preview stops working.
At this point I'm sure I've missed something important from the way this is supposed to be done, but after going through the Laravel docs and looking at online tutorials/examples, I've no idea what.
I've got everything working - though not entirely sure it's the "Laravel way".
If anyone's got suggestions for improving it - add a comment or new answer and I'll try it out.
Console\Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('email:expired-licences')
->weekdays()
->at('08:30');
}
App\Console\Commands\SendExpiredLicences.php:
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences';
protected $description = 'Email a list of expired licences to Admin';
private $mail;
public function _construct()
{
$clients = DB::select("[Insert SQL here]");
$this->mail = (new NotifyExpiredLicences($clients))->toMail('admin#example.com');
parent::__construct();
}
public function handle()
{
Mail::to('admin#example.com')->send($this->mail);
return 0;
}
public function preview()
{
return $this->mail;
}
}
App\Notifications\NotifyExpiredLicences.php:
class NotifyExpiredLicences extends Notification
{
public function __construct(protected $clients)
{
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new Mailable($this->clients));
}
}
App\Mail\ExpiredLicences.php:
class ExpiredLicences extends Mailable
{
public function __construct(private $clients)
{
}
public function build()
{
return $this
->subject('Clients with Expired Licences')
->markdown('emails/expiredlicences',
['clients' => $this->clients, 'toname' => 'Admin']);
}
}
resources\views\emails\expiredlicences.blade.php:
#component('mail::message')
# Hi {!! $toname !!},
#component('mail::table')
| Client | Expired |
| ------------- | --------:|
#foreach ($clients as $client)
|{!! $client->CompanyName !!} | {!! $client->Expired !!}|
#endforeach
#endcomponent
<hr />
Thanks, {!! config('app.name') !!}
#endcomponent
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
return (new SendExpiredLicences())->preview();
});
Ok just to save more commenting, here's what I'd recommend doing. This is all based on the Laravel docs, but there are multiple ways of doing it, including what you've used above. I don't really think of them as "right and wrong," more "common and uncommon."
Console\Kernel.php: I'd keep this mostly as-is, but pass the email to the command from a config file, rather than having it fixed in the command.
use App\Console\Commands\SendExpiredLicences;
…
protected function schedule(Schedule $schedule)
{
$recipient = config('myapp.expired.recipient');
$schedule->command(SendExpiredLicences::class, [$recipient])
->weekdays()
->at('08:30');
}
config/myapp.php:
<?php
return [
'expired' => [
'recipient' => 'admin#example.com',
],
];
App\Console\Commands\SendExpiredLicences.php: update the command to accept the email address as an argument, use on-demand notifications, and get rid of preview() method. Neither the command or the notification need to know about the client list, so don't build it yet.
<?php
namespace App\Console\Commands;
use App\Console\Command;
use App\Notifications\NotifyExpiredLicences;
use Illuminate\Support\Facade\Notification;
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences {recipient}';
protected $description = 'Email a list of expired licences to the given address';
public function handle()
{
$recip = $this->argument('recipient');
Notification::route('email', $recip)->notify(new NotifyExpiredLicences());
}
}
App\Notifications\NotifyExpiredLicences.php: the toMail() method should pass the notifiable object (i.e. the user getting notified) along, because the mailable will be responsible for adding the To address before the thing is sent.
<?php
namespace App\Notifications;
use App\Mail\ExpiredLicenses;
use Illuminate\Notifications\Notification;
class NotifyExpiredLicences extends Notification
{
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new ExpiredLicenses($notifiable));
}
}
App\Mail\ExpiredLicences.php: since the mail message actually needs the list of clients, this is where we build it. We get the recipient here, either from the user's email or the anonymous object.
<?php
namespace App\Mail;
use App\Models\Client;
use Illuminate\Notifications\AnonymousNotifiable;
class ExpiredLicences extends Mailable
{
private $email;
public function __construct(private $notifiable)
{
// this allows the notification to be sent to normal users
// not just on-demand
$this->email = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
}
public function build()
{
// or whatever your object is
$clients = Client::whereHas('licenses', fn($q)=>$q->whereExpired(1));
return $this
->subject('Clients with Expired Licences')
->markdown(
'emails.expiredlicences',
['clients' => $clients, 'toname' => $this->notifiable->name ?? 'Admin']
)
->to($this->email);
}
}
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
// create a dummy AnonymousNotifiable object for preview
$anon = Notification::route('email', 'no#example.com');
return (new ExpiredLicencesNotification())
->toMail($anon);
});

Laravel mailable, data not being passed to Blade when implements ShouldQueue

Im trying to send an email, with ShouldQueue, and passing data into the email blade view, but it fails if i implement ShouldQueue.
See below for my code.
The code as it is below, does execute the mail and places it in jobs queue (database driver). It works and i can see the job in jobs table.
Then i run php artisan queue:work to start worker on my computer.
When worker tries to execute job, it fails. It is now removed from jobs table and new entry to failed_jobs table. The email is not sent.
In failed_jobs table, exception column, i get this error (stacktrace removed)
Next Facade\Ignition\Exceptions\ViewException: Undefined variable $data (View: /Users/xxx/xxx/xxx/resources/views/mails/ContactForm.blade.php) in /Users/xxx/xxx/xxx/resources/views/mails/ContactForm.blade.php:2
IF I remove "implements ShouldQueue" from the ContactFormMail class, everything works and email is being sent, but then, the email is not being put in the queue.
I have tried passing data both ways described in Laravel docs and i have also tried restarting worker.
Any ideas where i go wrong?
If it was not clear, I want to pass data to ContactForm.blade.php and also place email in queue.
Here is my code
Controller:
public function submit()
{
$this->validate();
$data = [
'name' => $this->name,
'email' => $this->email,
'message' => $this->message,
];
try {
Mail::to('xxx#xxx.xx')->send(new ContactFormMail($data) );
$this->name = "";
$this->email = "";
$this->message = "";
$this->status = "Message sent!";
} catch (Exception $e) {
$this->status = "Something went wrong!";
}
}
ContactFormMail class:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class ContactFormMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('mails.ContactForm');
}
}
ContactForm blade file:
<div>
{{$data['name']}}<br>
{{$data['email']}}<br>
{{$data['message']}}<br>
</div>
Please try restarting the queue worker. Probably something got stuck. After restarting the queue it "should" work.
php artisan queue:restart

Laravel 7- How to pass data(variable) from CONTROLLER to JOB to MAIL to VIEW?

I have seen this question asked a few times without anyone really answering it, or their method did not work. I am going to paste what I have thus far but I am still getting an error that the variable could not be found. The job queue works just fine but can not find the variable in the view. Below the code are the other resources that I have viewed that did not work.
ERROR MESSAGE- ErrorException: Undefined variable: newNeed in /Users/JandB/Desktop/myProject/storage/framework/views/dab250ddee8692f8f6a1fa3334aad4ba0eb81350.php:1
CONTROLLER
public function store(StoreNeedRequest $request)
{
$users = User::where('team_id', '=', auth()->user()->team_id)->get();
$newNeed = 4;
foreach ($users as $user) {
SendNeedsEmailJob::dispatch($newNeed, $user);
}
}
JOB
class SendNeedsEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $newNeed;
protected $user;
public function __construct($newNeed, $user)
{
$this->newNeed = $newNeed;
$this->user = $user;
}
public function handle()
{
Mail::to($this->user->email)
->send(new NeedsMail($this->newNeed));
}
}
MAIL
class NeedsMail extends Mailable
{
use Queueable, SerializesModels;
public $newNeed;
public function __construct($newNeed)
{
$this->newNeed = $newNeed;
}
public function build()
{
return $this->markdown('emails.needs')->with('newNeed', $this->newNeed)->subject('New NEED');
}
}
VIEW
{{$newNeed}}
Other Resources
https://laracasts.com/discuss/channels/laravel/laravel-job-not-passing-variable-to-end-view https://medium.com/#petehouston/passing-data-to-blade-template-from-queue-in-laravel-79130c598f01 Laravel queues issues when I pass data from controller to jobs
Make sure you have removed any previous jobs, and cleared any caching so that you aren't running an older version of NeedsMail without the $newNeed variable defined.

Sending multiple email(to) using laravel mailable

I am using laravel 5.6 and trying to send email to multiple email address. Here my code works for only one email. How can I use multiple email?
I am using mailable
Here is my controller
public function sendEmail(Request $request)
{
Mail::send(new sendMail());
Session::flash('conf_email', 'ok');
return redirect('/send-email');
}
class SendMail extends Mailable
{
public function build(Request $request)
{
return $this->view('mail', ['msg' => $request->message])->to($request->to);
}
}
the to() function accepts multiple users (array of emails(strings) or objects)
please see the following link for more information
https://laravel.com/api/5.5/Illuminate/Contracts/Mail/Mailer.html#method_to
so you can do something like this
class SendMail extends Mailable
{
public function build(Request $request)
{
$users=User::all();
return $this->view('mail', ['msg' => $request->message])->to($users);
//or
return $this->view('mail', ['msg' => $request->message])->to(['test#test.com','test1#test.com']);
}
}
You just need to change sendEmail function to:
public function sendEmail(Request $request)
{
Mail::to(['test#test.com','test1#test.com'])->send(new sendMail());
Session::flash('conf_email', 'ok');
return redirect('/send-email');
}

Laravel Bulk Send Mail with Notification

I read in laravel documentation :
Once the ShouldQueue interface has been added to your notification, you may send the notification like normal. Laravel will detect the ShouldQueue interface on the class and automatically queue the delivery of the notification:
Because of in notification do automatically queue,
if I use in my controller:
public function store(Request $request)
{
$users = User::all()
Notification::send($users, new MyFirstNotification());
}
and in my notification:
public function toMail($notifiable)
{
return new custome-emailTo("emails.welcome",$notifiable);
}
and in custom-mailTo, (it is a mailable class) :
public function __construct($view2,User $user)
{
$this->user = $user;
$this->view = $view2;
}
public function build()
{
$this->to($this->user->email);
return $this->view('emails.welcome');
}
for me, it work and send to many users,
but my questions are:
As stated in the documentation of Laravel,
1. Do it really do queuing for send notification?
2. Do I need queue in mailable laravel class for send bulk email?

Resources