I'm working with laravel 7.0 and for some reason when I write subject in my build method of a mailable and queue it by calling the queue method of the Mail facade the subject isn't changing, it is taking the mailable classname and setting it as the subject. If I hard code the $subject variable in the mailable class it works and if I don't queue it then the subject gets set. Anybody knows how to solve this? I'm using database driver to handle the queue.
Mailable build method
public function build()
{
return $this->subject(config('app.name') . ' OTP')
->markdown('emails.otp');
}
How I'm queuing up the mail to send
\Illuminate\Support\Facades\Mail::to($this->user->email)
->queue(new \App\Mail\Otp($generatedOtp));
Related
I am using this package to try and send apn notifications in my Laravel app. However, I have followed the documentation on the main page, and when I try to send an apn notification, I can log on the server that the constructor and via methods are called, but I can't figure out why my notification either isn't being sent or isn't being received. My logs have no info from the package either.
How do I troubleshoot this? What am I missing?
MyNotification.php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use NotificationChannels\Apn\ApnChannel;
use NotificationChannels\Apn\ApnMessage;
class MyNotification extends Notification
{
use Queueable;
public function __construct()
{
Log::debug('MyNotification constructor called');
}
public function via($notifiable)
{
Log::debug('MyNotification via called');
return [ApnChannel::class];
}
public function toApn($notifiable)
{
Log::debug('MyNotification toApn called');
return ApnMessage::create()
->badge(1)
->title('My title')
->body('My body');
}
public function routeNotificationForApn($notifiable)
{
Log::debug('MyNotification routeNotificationForApn called');
return $notifiable->token;
}
}
usage code in MyController.php
public function sendNotification(MyModel $model)
{
// authorization checks here...
$devices = Device::where('user_id', $model->user_id)->get();
Notification::send($devices, new MyNotification());
return response()->json(null, 200);
}
Here is what my broadcasting.php and .env files look like:
Your notification uses Queueable so your notifications are only send if your queue is correctly setup.
You can run your queue locally by running
$ php artisan queue:work
On your console you would also get a feedback if your queued job (your notification) has been submitted successfully (not if it is delivered successfully).
This is also highlighted inside the Laravel docs
Before queueing notifications you should configure your queue and start a worker.
If you need a queue that is probably sth. you need to decide as only you know how much load is the application, how many notifications should be sent.
Taken from the docs:
Sending notifications can take time, especially if the channel needs to make an external API call to deliver the notification. To speed up your application's response time, let your notification be queued by adding the ShouldQueue interface and Queueable trait to your class.
Personal opinion: Make it work without a queue, f. ex. locally, and then add a queue.
If you configured to run your queue with Redis, I would highly recommend to use Laravel Horizon to monitor the jobs in your queue.
Tips for using APN
Configure the APN service correctly in config/broadcasting.php - see Github docs.
The problem was that the function routeNotificationForApn() belongs in the notifiable model (in my instance, Device), not in the MyNotification class.
Removing the use Queueable; is required as well if you don't have a queue set up.
I would like to send mail to user after creating account on my website and I would like to use queues to send them. I'm using PHP Laravel framework.
My controller handles the request after clicking on "Create account":
class LoginController extends Controller
{
...
public function register(Request $request) {
...
$mail = (new RegisterRequest($user))->onConnection("database")->onQueue("emailsQueue");
Mail::queue($mail);
...
}
}
Then I have this RegisterRequest (mailable) class:
class RegisterRequest extends Mailable
{
use Queueable, SerializesModels;
protected $user;
public function __construct($user)
{
$this->user = $user;
}
public function build()
{
return $this->from('user#example.com')
->to($this->user->email)
->subject("Confirm your Email Address")
->view('emails.register.request')
->with("registration_token", $this->user->registration_token);
}
}
As you can see, I am using relational database to store jobs. And really, after calling LoginController's register method, a job is saved to database. But it can't be processed. I also start php artisan queue:work but nothing is done with jobs in database. Any help?
EDIT:
So I just found out that picking jobs from queue is done by SQL selecting the 'default' queue name. But I'm sending mails to queue 'emailsQueue'. So I'm now running Queue Worker like this: php artisan queue:work --queue=emailsQueue and everything's working fine for now. But how can I pick jobs from every queue in database? It's probably not the best attempt, right? It wouldn't make any sense to have named queues, right? But let's say I have one queue for processing register account requests, another queue for changing password requests and so on... So I think it does make sense to process every queue. So how can I do this? Can it be done just by listing the queues like this?
php artisan queue:work --queue=registerAccountEmailQueue,changePasswordEmailQueue...
What exactly does running php artisan queue:work? I thought it's the command to run all queues.
Use queue driver database.
In controller you should write
$this->dispatch(new SendNotifyMail($data));
This will pass your $data to queue. here SendNotifyMail is used as Job Class. So you should also use this in Controller like use App\Jobs\SendNotifyMail;.
Then create a file in Folder Jobs, named SendNotifyMail
<?php
namespace App\Jobs;
use App\Jobs\Job;
use DB;
use Mail;
use Artisan;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendNotifyMail extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
public $timeout = 300; // default is 60sec. You may overwrite like this
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function handle(Mailer $mailer)
{
$data = $this->data; // retrieve your passed data to variable
// your mail code here
}
}
In your command you need to write
php artisan queue:listen
or
php artisan queue:work
Then execute the code.
I want to send a mail via laravel. For some reason, I only want to set the cc before calling the send method:
Mail::cc($cc_mail)->send(new MyMailAlert());
Then I define the recipient (to) directly in the build method of my Mailable class:
$this->subject($subject)->to($to_email)->view('my-mail');
But it fails:
Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined method Illuminate\Mail\Mailer::cc()
How can I send a mail without knowing the recipient before sending it in the build method? In other word I want to set the recipient (to) directly in the build method and I don't know how to do this.
cc is documented in Laravel Docs, but I can't find the method or property in the Illuminate\Mail\Mailer source code, neither in the Laravel API Documentation. So you can't use it this way.
But Illuminate\Mail\Mailable has the cc property. So, if you want to add the cc before sending and add the to on the build method, you need something like this:
MyMailAlert.php
class MyMailAlert extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct()
{
//
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->subject($this->subject)->to($this->to)->view('my-mail');
}
}
In your controller:
$myMailAlert = new MyMailAlert();
$myMailAlert->cc = $cc_mail;
// At this point you have cc already setted.
Mail::send($myMailAlert); // Here you sends the mail
Note that the build method uses subject and to properties of the mailable instance, so you have to set it before sending.
I'm not sure from where are you retrieving your $subject and $to_email in your build method example, but for my example you have to give these values to $myMailAlert->subject and $myMailAlert->to. You can use your custom variables in the build method, but given that the class already has these properties, custom variables aren't needed.
Here is a hack to deal with this problem:
Mail::to([])->cc($cc_mail)->send(new MyMailAlert());
So just add a to() method with an empty array and it works. It's still a hack, I'm not really sure it will work in the future.
I have no problem sending synchronous mails, but the ones that get executed on a queue using Mail::queue throw the next error:
Illuminate\Contracts\Container\BindingResolutionException: Target [Swift_Transport] is not instantiable while building [Illuminate\Mail\Mailer, Swift_Mailer]. in /var/www/myapp.dev/vendor/illuminate/container/Container.php:804
Everything is configured correctly as emails are being sent when I do it synchronously
After having so much trouble with sending emails inside of Lumen Jobs, I encapsulated the email sending in a Job which works with mailables like this:
MailDispatcher.php
<?php
namespace App\Jobs;
use Illuminate\Contracts\Mail\Mailable;
use Illuminate\Support\Facades\Mail;
class MailDispatcher extends Job {
public $mail;
public function __construct(Mailable $mail) {
$this->mail = $mail;
}
public function handle() {
Mail::send($this->mail);
}
}
Then when I want to queue a mail...
$mail = new MyMailableMail($user);
dispatch(new MailDispatcher($mail));
And it works correctly
In laravel you can call the ShouldQueue interface like so
class ProfileWasCreated extends Event implements ShouldQueue
By default this will queue the event on the default queue, but I can't seem to figure out how to queue this event on a different queue with the name email.
$this->onQueue('emails');
Add the line above in your constructor for the email class. It would set the queue name. Then you can just use the Mail::send() function and it would queue on the "emails" queue.
You can specify the queue where a job should be sent by calling onQueue() on the job object, e.g.:
$job = new MyJob();
$job->onQueue('queue_name');
$this->dispatch($job);
onQueue method is provided by Queueable trait - it should be already included in your base App\Jobs\Job class by default.
dispatch() method is provided by ** DispatchesJobs** trait that you should include in the class that you want to dispatch jobs.
Have a look here for more details on how to use jobs and queues: http://laravel.com/docs/5.1/queues#dispatching-jobs-from-requests