Weird behavior with Laravel Slack Notifications - php

So I am setting up a exception handler that send a Slack notification every time an exception happens in the application, I have also setup this when a Complaint(Model) is created and that work fine. But in my Exception/Handle.php I have this setup.
Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new ExceptionCreated($exception));
The webhook does exist and it does work. And while dd() every possible step I know that it hits the ExceptionCreated in Notifications. So there I have this code.
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
use Carbon\Carbon;
use Throwable;
use Auth;
use Illuminate\Support\Facades\Request;
class ExceptionCreated extends Notification
{
use Queueable;
/**
* #var Throwable
*/
public $exception;
/**
* Create a new notification instance.
*
* #param \Throwable $exception
* #return void
*/
public function __construct(Throwable $exception)
{
$this->exception = $exception;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['slack'];
}
/**
* Get the slack representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\SlackMessage
*/
public function toSlack($notifiable)
{
dd($this->exception);
//and a lot of code here but that doesn't matter
}
Now when I dump inside the constructor I can see the exception is being passed correctly, but it never fires the toSlack function and I have no idea why, I have cleared cache, dump autoload and nothing seems to be working, as I said I have this exact code for my Complaints and that works fine. So any suggestions are much appreciated.

Related

Laravel 8: Undefined property: App\Notifications\ResetPassword::$queue

I want tp apply queue for my Notifications, so I implemented that at my Notification which ResetPassword:
class ResetPassword extends Notification implements ShouldQueue
Then I ran php artisan queue:table and migrate it so the table jobs created successfully at the DB.
And also change the QUEUE_CONNECTION to database at .env file and re-run php artisan serve.
But when I test this and clicked on reset password link, a new table row must be added to jobs table but it does not.
And instead of that, this error returns:
ErrorException Undefined property:
App\Notifications\ResetPassword::$queue
...\notification\vendor\laravel\framework\src\Illuminate\Notifications\NotificationSender.php:195
So what is going wrong here ? How can I fix this issue ?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
UPDATE #1:
ResetPassword.php:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;
class ResetPassword extends Notification implements ShouldQueue
{
/**
* The password reset token.
*
* #var string
*/
public $token;
/**
* The callback that should be used to build the mail message.
*
* #var \Closure|null
*/
public static $toMailCallback;
/**
* Create a notification instance.
*
* #param string $token
* #return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's channels.
*
* #param mixed $notifiable
* #return array|string
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
return (new MailMessage)
->subject('subject goes here')
->line('This email is sent to you')
->action(Lang::get('Reset Password'), url(config('app.url').route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::get('Until the next 60 minutes you can use this link', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::get('If you did not request a password reset, no further action is required.'));
}
/**
* Set a callback that should be used when building the notification mail message.
*
* #param \Closure $callback
* #return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}
Look Like you have forgot to use Queueable Trait in your notification
use Illuminate\Bus\Queueable;
class ResetPassword extends Notification implements ShouldQueue
{
use Queueable;
Queueable trait has propety $queue
Ref:https://laravel.com/docs/8.x/notifications#queued-notifications-and-database-transactions

Laravel Slack Notification not sent when using implements ShouldQueue

I am struggling with dispatching a Slack notification when the Notification class implements ShouldQueue.
This is how I dispatch the notification
/**
* Handles the sendout of booking request confirmation to the customer
*
* #return void
*/
public function sendCustomerNotifications()
{
$this->booking->customer->notify((new CustomerBookingRequested($this->booking)));
}
This is how I my CustomerBookingRequested notification class looks like
class CustomerBookingRequested extends Notification implements ShouldQueue
{
use Queueable;
private $booking;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Booking $booking)
{
//
$this->booking = $booking;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail','slack'];
}
...
//code for toMail
...
/**
* Get the Slack representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Message\SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->success()
->content('New booking requested!');
}
My Customer Model uses Notifiable
class Customer extends Model implements HasLocalePreference
{
use HasFactory;
use Billable;
use SoftDeletes;
use Notifiable;
...
I also added to my Customer Model the routing method
/**
* Route notifications for the Slack channel.
*
* #param \Illuminate\Notifications\Notification $notification
* #return string
*/
public function routeNotificationForSlack($notification)
{
return env('SLACK_WEBHOOK');
}
When I remove implements ShouldQueue from my Notification class, both Slack and Mail Message is sent. When I keep implements ShouldQueue, the Mail message is sent, Slack message is not sent.
I basically want to send the customer a mail notification with a booking confirmation. At the same time I want to send a Slack message to the team's slack workspace. That's why I just added a static webhook URL in the customer model which is linked to the company's slack workspace.
I am a bit stuck here. Probably it's something obvious, but I can't find what I do wrong.
Thanks for your support!
Using Laravel 8.0 with "laravel/slack-notification-channel": "^2.3"
Move the SLACK_WEBHOOK env variable to a config.
File: config/sample.php
return [
'url' => env('SLACK_WEBHOOK')
];
Then reference the config from the Customer model.
File: app/Models/Customer.php
public function routeNotificationForSlack($notification) {
return config('sample.url');
}

My custom Mail Notification isn't sending while all the other are

I have a weird issue in my website. I have several Notifications such as Email Verification and Password Reset that are sending properly. However, I made my own notification that sends an url with a UUID to the user and unfortunately, it doesn't send.
I tried every way: 'Notification::route', notify the user directly, nothing works. Actually, notifying the user directly would be bad since I need to send it to an email address not attached to any model.
Anyway, here's the code for the notification. Keep in mind the other notifications work, so I doubt it is the issue.
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewEmail extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($uuid)
{
$this->uuid = $uuid;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line(__('messages.newEmailAsked'))
->action(__('messages.newEmailConfirm'), config('app.frontend_url') . '/verify-new-email?code=' . $this->uuid)
->line(__('messages.newEmailIgnore'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
public function addNewEmail($email) {
if(User::where('email', $email)->count() === 0) {
$uuid = Str::uuid();
NewEmail::create(['email' => $email, 'unique_code' => $uuid, 'user_id' => $this->id]);
Notification::route('mail', $email)->notify(new \App\Notifications\NewEmail($uuid));
} else {
return 'Email already exists.';
}
}
I really don't get why this notification isn't sent while the other are...
Weirdly enough, the problem fixed itself when I did the actual path the user would take instead of using Tinker.
Just had to $user->addNewEmail($newEmailHere) and it worked properly...
You have not defined the $uuid variable in your Queueable class

Laravel 5.7 Multilogin SendEmailVerificationNotification error

I'm new to Laravel. I just created a custom login with laravel 5.7. When I tried to reset password I'm getting this error:
"Declaration of
App\Employee::sendEmailVerificationNotification($token) should be
compatible with
Illuminate\Foundation\Auth\User::sendEmailVerificationNotification()"
Does anyone know how to resolve this error?
You may do something like this
class Employee extends Model implements MustVerifyEmail {
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
}
if you want to call it like Employee::sendEmailVerificationNotification() and if you want to verify the token you should extend the VerifyEmail notification something like
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Auth\Notifications\VerifyEmail;
class VerifyEmailNotification extends VerifyEmail
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($token)
{
//verify token
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable);
}
return (new MailMessage)
->subject(Lang::getFromJson('Verify Email Address'))
->line(Lang::getFromJson('Please click the button below to verify your email address'))
->action(
Lang::getFromJson('Verify Email Address'),
$this->verificationUrl($notifiable)
)
->line(Lang::getFromJson('If you did not create an account, no further action is required.'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
Then in Employee model
public function sendEmailVerificationNotification($token)
{
$this->notify(new VerifyEmailNotification($token)); // your custom notification
}
You have to follow the same method signature if you want to override it-
You are overriding this method-
Illuminate\Foundation\Auth\User::sendEmailVerificationNotification()
to this-
App\Employee::sendEmailVerificationNotification($token)
If you notice the difference, you have passed $token in the method while the original method definition does not support that.
Create a different method if you need a different signature from the original method.

No query results for model [App\Models\Match]

I'm building an API with Laravel and want to send push notification using the Laravel Notifications system. I've a model for matches (which is basically a post), another user can like this match. When the match is liked, the creator of the post will get a push notification. It's just like Instagram, Facebook, etc.
Often the push notification wasn't send to the user. I installed Laravel Horizon to see if there where errors. Sometimes the notification was send and sometimes it wasn't. With the exact same data:
The notification fails sometimes with the exact same data (same user, same match).
The error is as followed:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results
for model [App\Models\Match] 118 in
/home/forge/owowgolf.com/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:312
I'm sure the match and the user exists in the database, I've verified that before sending the notification. Does anybody know what's going wrong? Everything I could find online is that people didn't save their model before sending the notification into the queue. But the line where the code send's the notification into the queue wouldn't even be reached if the model didn't exists. Because of Implicit Binding in the route/controller.
Controller method:
/**
* Like a match.
*
* #param \App\Models\Match $match
* #return \Illuminate\Http\JsonResponse
*/
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$user->notify(new NewLikeOnPost($match, currentUser()));
}
return ok();
}
Notification:
<?php
namespace App\Notifications;
use App\Models\Match;
use App\Models\User;
use Illuminate\Bus\Queueable;
use NotificationChannels\Apn\ApnChannel;
use NotificationChannels\Apn\ApnMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class NewLikeOnPost extends Notification implements ShouldQueue
{
use Queueable;
/**
* The match instance.
*
* #var \App\Models\Match
*/
private $match;
/**
* The user instance.
*
* #var \App\Models\User
*/
private $user;
/**
* Create a new notification instance.
*
* #param \App\Models\Match $match
* #param \App\Models\User $user
*/
public function __construct(Match $match, User $user)
{
$this->user = $user;
$this->match = $match;
$this->onQueue('high');
}
/**
* Get the notification's delivery channels.
*
* #param \App\Models\User $notifiable
* #return array
*/
public function via($notifiable)
{
if ($notifiable->wantsPushNotification($this)) {
return ['database', ApnChannel::class];
}
return ['database'];
}
/**
* Get the mail representation of the notification.
*
* #param \App\Models\User $notifiable
* #return \NotificationChannels\Apn\ApnMessage
*/
public function toApn($notifiable)
{
return ApnMessage::create()
->badge($notifiable->unreadNotifications()->count())
->sound('success')
->body($this->user->username . ' flagged your match.');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'user_id' => $this->user->id,
'body' => "<flag>Flagged</flag> your match.",
'link' => route('matches.show', $this->match),
'match_id' => $this->match->id,
];
}
/**
* Get the match attribute.
*
* #return \App\Models\Match
*/
public function getMatch()
{
return $this->match;
}
}
This is not a complete solution, but it will lower your chances of running into this error in the future.
Instead of passing in the whole Match model into the job, only pass the id of the model. You can then fetch that model in the constructor.
/**
* Like a match.
*
* #param \App\Models\Match $match
* #return \Illuminate\Http\JsonResponse
*/
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$user->notify(new NewLikeOnPost($match->id, currentUser()->id));
}
return ok();
}
Notification:
class NewLikeOnPost extends Notification implements ShouldQueue
{
use Queueable;
private const QUEUE_NAME = 'high';
/**
* The match instance.
*
* #var \App\Models\Match
*/
private $match;
/**
* The user instance.
*
* #var \App\Models\User
*/
private $user;
/**
* Create a new notification instance.
*
* #param int $match
* #param int $user
*/
public function __construct(int $matchId, int $userId)
{
$this->user = User::query()->where('id', $userId)->firstOrFail();
$this->match = Match::query()->where('id', $matchId)->firstOrFail();
$this->onQueue(self::QUEUE_NAME);
}
// Rest of the class is still the same...
}
You can use the SerializesModels trait, but it doesn't work well when you add a delay to a queued job. This is because it will try to reload the model on __wakeup() and sometimes it cannot find the class.
Hopefully this helps :)
Its probably because $user is not an object of User model, its an object of Match model. You need to do a User::findorfail or User::firstOrFail then notify the user.
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$someUser = User::findOrFail($user->user_id);
$someUser->notify(new NewLikeOnPost($match, currentUser()));
}
return ok();
}
Unless the notify trait is used in Match model. Or you could use eager loading which will cost way less queries!
Check your .env to be sure that u really use REDIS
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120
QUEUE_DRIVER=redis
then clear cache ( php artisan cache:clear , php artisan view:clear ), that should clear the issue
EDIT
I had similar problems but now I use Docker only and before I had to check for cached configfiles, wrong file/folderpermissions and so on (REDIS for broadcast only, others were standard). I started using redis only - that`s a lot easier, faster and more debugfriendly for me ! And together with Docker really helpful to not use messed up nginx/apache/php/redis/ ...

Categories