Well im testing this new notification stuff implemented in laravel 5.3 and its great,
i have this notification class which sends a mail to the authenticated user (when he hits a specific route) which is the default code.
notification
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class notifyme extends Notification implements ShouldQueue
{
use Queueable;
public function __construct()
{
//
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', 'https://laravel.com')
->line('Thank you for using our application!');
}
This is the controller functions that instantiates the notification class
public function notifyme()
{
$user = Auth::user()
$user->notify(new notifyme($user));
//$user->notify((new notifyme($user))->delay(Carbon::now()->addMinutes(10)));
return redirect('/home');
}
now while using a ubuntu os , and setting my queue driver as sync which should work fine on localhost QUEUE_DRIVER="sync"
i started a worker php artisan queue:work
But nothing shows on the terminal windows also page still a bit slow (queues are not working)
i have the default queue.php and i didnt change it, and as i mentioned, im using sync as a driver
Any suggested solution?
sync driver doesn't use queues, it allows to run jobs synchronously for running tests for examle.
You need to use one of the driver provided by laravel listed here - Laravel queues, or install some custom like RabbitMQ or something else
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 have a working project written in laravel, i've integrated Sentry to monitor for errors, i followed instructions on their site, basically just
composer require sentry/sentry-laravel
and in my App/Exceptions/Handler.php
if (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->captureException($exception);
}
Now this works fine, i see errors in sentry.
But at the other end of the app wherever i use queue, for example i have "forgot password" notification and it implements ShouldQueue class, practically:
<?php
namespace App\Notifications\Auth;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class PasswordResetRequest extends Notification implements ShouldQueue
{
use Queueable;
protected $token;
public function __construct($token)
{
$this->token = $token;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
$url = url('/password/find/'.$this->token);
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url($url))
->line('If you did not request a password reset, no further action is required.');
}
}
And when triggered works as expected, i have a new record in jobs table (i have database as queue driver)
now when i start my worker
php artisan queue:work
it stalls it works last job over and over
there is no mail sent or received...
By the way this is only when i have reporting in sentry, if i remove those lines in App/Exceptions/Handler.php in report() method it's all good, also if i remove queue from notification it works, only the combinatio of sentry error reporting and notification queuing is making me a big problem.
Unfortunately, Sentry-laravel doesn't work with queues.
Please, see this issue on their GitHub repo to get more info about it:
https://github.com/getsentry/sentry-laravel/issues/18
There is another issue (open) where they are trying to fix it:
https://github.com/getsentry/sentry-laravel/pull/228
If you really need to put these logs on a queue now you should use https://github.com/twineis/raven-php.
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 work on a website project with the laravel framework and I want when I click on the button send a notification either send to the user's email
$invite = Invite::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'token' => str_random(60),
]);
$invite->notify(new UserInvite());
tnx to help me
What you are using is mail notification, Here is the answer but you can refer to notification section of laravel docs for more information:
https://laravel.com/docs/5.4/notifications
first generate the notification using your terminal in project folder:
php artisan make:notification UserInvite
Then in generated file specify your driver to be 'Mail'. byr default it is. Also laravel has a nice sample code there. And it is better you inject your $invite to the notification so you can use it there. here is a quick sample code. You can find the notification generated under App\Notifications.
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use App\Invite;
class UserInvite extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct()
{
//
}
public function via($notifiable)
{
return ['mail']; // Here you specify your driver, your case is Mail
}
public function toMail($notifiable)
{
return (new MailMessage)
->greeting('Your greeting comes here')
->line('The introduction to the notification.') //here is your lines in email
->action('Notification Action', url('/')) // here is your button
->line("You can use {$notifiable->token}"); // another line and you can add many lines
}
}
now you can call your notification:
$invite->notify(new UserInvite());
since you are notifying on invite, your notifiable is the same invite. As a result in your notification you can use $notification->token to retrieve the token of invite object.
Please let me know if I can be of any help.
regards.
I am looking for a working solution, to translate queued emails in laravel-5.
Unfortunately, all emails use the default locale (defined under app.locale).
Let's assume, we have two emails in the pipeline, one for an English en user and another for an Japanese jp user.
What data should I pass to the Mail facade to translate (localize) the queued emails?
// User model
$user = User:find(1)->first();
Mailer::queue($email, 'Party at Batman\'s cave (Batcave)', 'emails.party-invitation', [
...
'locale' => $user->getLocale(), // value: "jp", but does not work
'lang' => $user->getLocale(), // value: "jp", but does not work
'language' => $user->getLocale(), // value: "jp", but does not work
]);
I have been struggling to get this done in a more efficient way. Currently I have it set up like this. Hopefully this helps someone in the future with this issue:
// Fetch the locale of the receiver.
$user = Auth::user();
$locale = $user->locale;
Mail::queue('emails.welcome.template', ['user' => $user, 'locale' => $locale], function($mail) use ($user, $locale) {
$mail->to($user->email);
$mail->subject(
trans(
'mails.subject_welcome',
[], null, $locale
)
);
});
And use the following in your template:
{{ trans('mails.welcome', ['name' => ucfirst($user['first_name'])], null, $locale) }}
Note: do not forget to restart your queue
If your emails inherits the built-in Illuminate\Mail\Mailable class you can build your own Mailable class that will take care of translations.
Create your own SendQueuedMailable class that inherits from Illuminate\Mail\SendQueuedMailable.
Class constructor will take current app.location from config and remember it in a property (because laravel queues serializes it).
In queue worker it will take the property back from config and set the setting to the current environment.
<?php
namespace App\Mail;
use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Mail\SendQueuedMailable as IlluminateSendQueuedMailable;
class SendQueuedMailable extends IlluminateSendQueuedMailable
{
protected $locale;
public function __construct(MailableContract $mailable)
{
parent::__construct($mailable);
$this->locale = config('app.locale');
}
public function handle(MailerContract $mailer)
{
config(['app.locale' => $this->locale]);
app('translator')->setLocale($this->locale);
parent::handle($mailer);
}
}
Then, create your own Mail class that inherits from Illuminate\Mail\Mailable
<?php
namespace App\Mail;
use Illuminate\Contracts\Queue\Factory as Queue;
use Illuminate\Mail\Mailable as IlluminateMailable;
class Mailable extends IlluminateMailable
{
public function queue(Queue $queue)
{
$connection = property_exists($this, 'connection') ? $this->connection : null;
$queueName = property_exists($this, 'queue') ? $this->queue : null;
return $queue->connection($connection)->pushOn(
$queueName ?: null, new SendQueuedMailable($this)
);
}
}
And, voila, all your queued mailables inherited from App\Mailable class automatically will take care of current locale.
In Laravel 5.6 is a locale function added to Mailable:
$infoMail->locale('jp');
Mail::queue($infoMail);
Laravel 5.7.7 introduced the HasLocalePreference interface to solve this issue.
class User extends Model implements HasLocalePreference
{
public function preferredLocale() { return $this->locale; }
}
Now if you use Mail::to() function, Laravel will send with the correct locale.
Also introduced was the Mail chainable locale() function.
Mail::to($address)->locale($locale)->send(new Email());
Here's a solution that worked for me. In my example, I am processing the booking on a queue, using a dedicated Job class.
When you dispatch a job on a queue, pass the locale, you want your email in. For example:
ProcessBooking::dispatch($booking, \App::getLocale());
Then, in your job class (ProcessBooking in my example) store the locale in a property:
protected $booking;
protected $locale;
public function __construct(Booking $booking, $locale)
{
$this->booking = $booking;
$this->locale = $locale;
}
And in your handle method temporarily switch locales:
public function handle()
{
// store the locale the queue is currently running in
$previousLocale = \App::getLocale();
// change the locale to the one you need for job to run in
\App::setLocale($this->locale);
// Do the stuff you need, e.g. send an email
Mail::to($this->booking->customer->email)->send(new NewBooking($this->booking));
// go back to the original locale
\App::setLocale($previousLocale);
}
Why do we need to do this?
Queued emails get the default locale since queue workers are separate Laravel apps. If you ever ran into a problem, when you cleared your cache, but queued 'stuff' (e.g. emails) still showed the old content, it's because of that. Here's an explanation from Laravel docs:
Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers.
using preferredLocale() is the "official" way to go.
you can implement HasLocalePreference and add method preferredLocale() to the notifiable model (as #Peter-M suggested):
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Get the user's preferred locale.
*
* #return string
*/
public function preferredLocale()
{
return $this->locale;
}
}
and then you just send the notification to that model and locale will be automatically applied to your email template
$user->notify(new InvoicePaid($invoice));
This also works with queued mail notifications.
read more here:
https://floyk.com/en/post/how-to-change-language-for-laravel-email-notifications
I'm faced with the same problem
Without extending the queue class the quickest / dirtiest solution would be to create an email template for each locale.
Then when you create the queue, select the local template i.e.
Mail::queue('emails.'.App::getLocale().'notification', function($message)
{
$message->to($emails)->subject('hello');
});
If the local is set to IT (italian) This will load the view emails/itnotification.blade.php
As I said... Dirty!
Since this method is actually horrible I came across this answer
And its working for me, you actually send the entire translated html version of the email in a variable to the queue and have a blank blade file that echos out the variable when the queue runs.