Laravel. How to get id of database notification? - laravel

I use database notifications, in notification code I have method toDatabase:
public function toDatabase($notifiable)
{
$user = \App\SomeUsers::where('id', $notifiable->id)->first();
return [
'message' => $message,
];
}
it returns data array which is being sent to database channel mentioned in via method of current notification:
public function via($notifiable)
{
return ['database'];
}
Everything is as usual, BUT... The problem is I need id of notification in database here in current notification file so that I could broadcast message (from current notification file) to frontend which contains id of notificaion in db (so I could somehow identify it to mark as read). How to get it?
P.S. Moreover, database notification may be queueable, so... it seems that I can't get id...
P.P.S Another words I need broadcast message which contains ["id" => "id of just added corresponding database notification"].

<?php
namespace App\Notifications;
use App\Channels\SocketChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Redis;
class MyCustomNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($param)
{
$this->param = $param;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
$channels = ['database'];
return $channels;
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toDatabase($notifiable)
{
info("This is the current notification ID, it's generated right here before inserting to database");
info($this->id);
return [
'id' => **$this->id**,
'message' => 'Notification message',
];
}
}
$this->id solves the problem.
https://laracasts.com/discuss/channels/laravel/get-database-notification-id-in-push-notification
P.S. I want to draw attention to one fact. When I posted this question, I knew about $this->id, but I couldn't make it work. The reason was: when I dive deeper to my target code from the top level I made changes to code, but they didn't apply. The reason is queues. You need to restart laravel worker to apply settings as Laravel caches logic or you need temporarily delete those: implements ShouldQueue and use Queueable.

In order to retrieve the actual ID of the notifications table in Laravel, you need to cast the ID column to string. First, you need to create a new model called, Notification.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Notification extends Model
{
/**
* Cast variables to specified data types
*
* #var array
*/
protected $casts = [
'data' => 'array',
'id' => 'string'
];
}
This way, if you retrieve the model, it will give you the actual ID of the table.
{
"id": "212829d6-5579-449f-a8e5-e86f0a08e0f9",
"type": "App\\Notifications\\CronFailureNotification",
....
}

The accepted answer did not provide a solution for me in Laravel 8. To properly get the id of the notification, listen for the NotificationSent event and retrieve the id there. Example code:
EventServiceProvider.php
protected $listen = [
NotificationSent::class => [
DispatchBroadcastNotification::class
]
];
DispatchBroadcastNotification.php
<?php
namespace App\Listeners;
use App\Notifications\BroadcastNotification;
use Illuminate\Notifications\Events\NotificationSent;
class DispatchBroadcastNotification
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param NotificationSent $event
* #return void
*/
public function handle(NotificationSent $event)
{
$event->notifiable->notify(
new BroadcastNotification($event->notification->id)
);
}
}
BroadcastNotification.php
<?php
namespace App\Notifications;
use App\Models\Tenant\User;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class BroadcastNotification extends Notification implements ShouldBroadcast
{
public $notificationId;
public function __construct($notificationId)
{
$this->notificationId = $notificationId;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via(User $notifiable): array
{
return ['broadcast'];
}
public function toBroadcast(User $notifiable)
{
return new BroadcastMessage([
'notificationId' => $this->notificationId
]);
}
}

Related

Broadcast event to Pusher when database notification is created

This is related to an earlier question I asked: Am I overcomplicating events/listeners/notifications?.
Based on the feedback received on that question, I modified my approach to attempt to do the following:
Fire a database notification.
Listen for a new database notification to be created
Fire an event and broadcast that to pusher.
So first I send the database notification:
Notification::send($this->requestedBy, new SendMonthlySummaryCreatedNotification($fileDownloadUrl, $fileName));
That notification class looks like this:
class SendMonthlySummaryCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;
public $fileDownloadUrl;
public $fileName;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($fileDownloadUrl, $fileName)
{
$this->fileDownloadUrl = $fileDownloadUrl;
$this->fileName = $fileName;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'title' => 'Monthly Summary Complete',
'message' => "{$this->fileName} is ready. ",
'link' => $this->fileDownloadUrl,
'link_text' => 'Click here to download',
'show_toast' => true,
'user_id' => $notifiable->id
];
}
}
I found this example of how to add a $dispatchesEvents property to a model in the docs, which I modified to apply it to a new model I created that extends the DatabaseNotification class, which I learned about on this SO question.
class Notification extends DatabaseNotification
{
use HasFactory;
protected $dispatchesEvents = [
'created' => NotificationCreatedEvent::class
];
public function users()
{
return $this->belongsTo(User::class, 'notifiable_id');
}
}
Theoretically the above should dispatch an event when my notification is sent, and then I have the NotificationCreatedEvent that I'd like to use to broadcast to pusher:
class NotificationCreatedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
protected $notification;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Notification $notification)
{
$this->notification = $notification;
Log::debug($this->notification);
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('users.' . $this->notification->notifiable_id);
}
}
The problem is that everything is working up until the NotificationCreatedEvent It doesn't seem to be getting fired. I don't know if I need to do anything in addition to mapping it in my new Notification model, or if it should just work.
My goal is to add a database notification, and then whenever that happens to send it to pusher so I can notify the user in real-time. This seems like it should work, but I'm not seeing anything coming over in pusher.

Laravel Echo with Pusher and Livewire - not receiving notifications client-side

I'm able to broadcast a notification to Pusher, but I'm unable to receive the response back in my livewire component.
Here is my Notification class:
<?php
namespace App\Notifications;
use App\Models\Statement;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class StatementCompletedNotification extends Notification implements ShouldQueue, ShouldBroadcast
{
use Queueable;
public $statement;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Statement $statement)
{
$this->statement = $statement;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database', 'broadcast'];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'user_id' => $this->statement->uploadedBy->id,
'statement_id' => $this->statement->id,
'file_name' => $this->statement->original_file_name
];
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('users.' . $this->statement->uploadedBy->id);
}
}
And here is the getListeners() method on my Livewire component. I've tried several different things here, first off I tried the way it's shown in the docs, just by referencing my StatementCompletedNotification in the listener, like so:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},StatementCompletedNotification" => 'refreshNotifications'
];
}
I noticed that in pusher, my event type is listed as Illuminate\Notifications\Events\BroadcastNotificationCreated, and I found this post online, so I tried that method like so:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},.Illuminate\\Notifications\\Events\\BroadcastNotificationCreated" => 'refreshNotifications'
];
}
Neither way has worked for me.
Here's where I'm attempting to just get something back in my javascript on the client-side:
Echo.private('users.1')
.notification((notification) => {
console.log(notification);
});
I don't get any response and I have no idea what the problem is. I've spent an insane amount of time on this and can't seem to figure it out.
Also, it appears that my notification is being picked up multiple times in the queue:
A little more background, the flow that I have set up right now is basically:
StatementCompleted event gets fired (not queued), there's a listener that handles the StatementCompleted event which is queued, and calls my StatementCompletedNotification class like so:
public function handle($event)
{
$event->statement->uploadedBy->notify(new StatementCompletedNotification($event->statement));
}

Why is the data column empty? Laravel Notifications

I am trying to set up database notification, but the data I am sending for the 'data' column isn't showing up...
My notification class:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use App\User;
use App\Admin;
class UserAddedToStudio extends Notification implements ShouldQueue
{
use Queueable;
public $user;
public $admin;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(User $user, Admin $admin)
{
$this->user = $user;
$this->admin = $admin;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail', 'database'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Welcome to '.$this->admin->name.'\'s studio')
->line('Hi '.$this->user->name.'. Your request to join '.$this->admin->name.'\'s studio has been accepted! Now you can start registering for lessons.')
// ->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toDatabase($notifiable)
{
return [
'message' => 'You have been added to the studio'
];
}
}
In my notifications table on MySQL all the columns are filled except for the data column. There I only get an empty array []. but why?? so frustrating.... :(
Here is the screenshot from my phpmyadmin.
Try to use toArray() instead of toDatabase()
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'message' => 'You have been added to the studio'
];
}
as you use Queue you should restart the queue worker.
php artisan queue: work
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. In addition, remember that any static state created or modified by your application will not be automatically reset between jobs.

Laravel 5.8 on demand notification error Call to a member function create() on null

When I do this, the user receives email without error:
Notification::send($user, new TicketNotification($details));
But, when I do this, the user also receives an email, but with an error in the screenshot below
Notification::route('mail', 'email_of_non-db_user')->notify(new TicketNotification($details));
Error: Call to a member function create() on null
Do have any idea why? How can I avoid this error?
I have to use On Demand Notification because I need to send a notification to someone who is not stored as a "user".
i think try this one
in TicketNotification update via method with this for only send to mail.
But u r also saved notification into database..
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
Thanks Jignesh, your answer works.
Sorry Thamer, I should have posted the whole code from the beginning.
Before, it was :
return ['mail','database'];
Now only :
return ['mail'];
Then, there is no error anymore.
Here my TicketNotification that made the error:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class TicketNotification extends Notification
{
use Queueable;
private $details;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($details)
{
$this->details = $details;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail','database'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject($this->details['subject'])
->greeting($this->details['title'])
->line($this->details['body'])
->line($this->details['links'])
;
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toDatabase($notifiable)
{
return [
'order_id' => $this->details['order_id']
];
}
}
Add this to your via method to use the same Notification for all your issues:
public function via($notifiable)
{
$availableChannels = [
'mail' => 'mail',
'database' => 'database',
'slack' => 'slack',
'telegram' => TelegramChannel::class
];
$channels = [];
foreach ($availableChannels AS $channel => $driver) {
if ($notifiable->routeNotificationFor($channel)) {
$channels[] = $driver;
}
}
return $channels;
}
You can now use On-Demand Notifications or fire the notificaton on users, without having to make multiple Notifications for each Channel or ON-DEMANDS etc...

laravel job/notification failing

I am trying to set up a contact form on my site whereby when someone clicks send, then a job is run and in that job, a notification is sent to all admin users. I keep getting this error in my failed jobs table though:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Contact]. in /var/www/html/leonsegal/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:412
I have been all over my code and I can't see what I have done wrong. Would anyone be able to help please?
Here is my controller:
<?php
namespace App\Http\Controllers;
use App\Contact;
use App\Jobs\SendContactJob;
class ContactController extends Controller
{
/**
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create()
{
return view('contact');
}
public function store()
{
request()->validate([
'name' => 'required|max:255',
'email' => 'required|email|unique:contacts|max:255',
'message' => 'required|max:2000',
]);
$contact = Contact::create(
request()->only([
'name',
'email',
'message',
])
);
SendContactJob::dispatch($contact);
return back()->with('success', 'Thank you, I will be in touch as soon as I can');
}
}
my job:
<?php
namespace App\Jobs;
use App\Contact;
use App\Notifications\SendContactNotification;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Notification;
class SendContactJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $contact;
/**
* Create a new job instance.
*
* #param Contact $contact
*/
public function __construct(Contact $contact)
{
$this->contact = $contact;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$users = User::all()
->where('admin', 1)
->where('approved', 1);
Notification::send($users, new SendContactNotification($this->contact));
}
}
my notification:
<?php
namespace App\Notifications;
use App\Contact;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class SendContactNotification extends Notification implements ShouldQueue
{
use Queueable;
protected $contact;
/**
* Create a new notification instance.
*
* #param $contact
*/
public function __construct(Contact $contact)
{
$this->contact = $contact;
}
/**
* 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($this->contact->name)
->line($this->contact->email)
->line($this->contact->message);
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
The weird thing is that when I run a die dump in the handle method of the job, it never fires, but the artisan queue worker says it was processed correctly but the subsequent notification is where it is failing. I am not sure why that handle method in the job wouldn't be firing.
I have set my .env file to database queue driver.
I thought it might be that I didn't import the contact model, but you can see I have.
Any help would be appreciated.
Could be that because both the job and the notification are queued, the contact could be getting 'lost in transit' so to speak. try making the job non queueable, and only queue the notification (or the other way around). Or scrap the job altogether and just send the notification from the controller.
Did you check on your model path? Coz for newer laravel the path should be
use App\Models\Contact;

Resources