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/ ...
Related
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
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
I am trying to do something that seems to go out of the box with how laravel-nova works ...
I have a Batch model/ressource that is used by super admins. Those batch reeports belongs to sevral merchants. We decided to add a layer of connection to are portal and allow merchants to log in and see there data. So obviously, when the merchant visites the batch repport page, he needs to see only data related to it's own account.
So what we did was add the merchant id inside the batch page like this:
nova/resources/batch?mid=0123456789
The problem we then found out is that the get param is not send to the page it self but in a subpage called filter ... so we hacked it and found a way to retreive it like this:
preg_match('/mid\=([0-9]{10})/', $_SERVER['HTTP_REFERER'], $matches);
Now that we have the mid, all we need to do is add a where() to the model but it's not working.
Obviously, this appoach is not the right way ... so my question is not how to make this code work ... but how to approche this to make it so that merchants can only see his own stuff when visiting a controller.
All i really need to is add some sort of a where('external_mid', '=' $mid) and everything is good.
The full code looks like this right now:
<?php
namespace App\Nova;
use App\Nova\Resource;
use Laravel\Nova\Fields\ID;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\Currency;
use Laravel\Nova\Fields\BelongsTo;
use App\Nova\Filters\StatementDate;
use Laravel\Nova\Http\Requests\NovaRequest;
class Batch extends Resource
{
/**
* The model the resource corresponds to.
*
* #var string
*/
//
public static function query(){
preg_match('/mid\=([0-9]{10})/', $_SERVER['HTTP_REFERER'], $matches);
if (isset($matches['1'])&&$matches['1']!=''){
$model = \App\Batch::where('external_mid', '=', $matches['1']);
}else{
$model = \App\Batch::class;
}
return $model;
}
public static $model = $this->query();
/**
* The single value that should be used to represent the resource when being displayed.
*
* #var string
*/
public static $title = 'id';
/**
* The columns that should be searched.
*
* #var array
*/
public static $search = [
'id','customer_name', 'external_mid', 'merchant_id', 'batch_reference', 'customer_batch_reference',
'batch_amt', 'settlement_date', 'fund_amt', 'payment_reference', 'payment_date'
];
/**
* Indicates if the resource should be globally searchable.
*
* #var bool
*/
public static $globallySearchable = false;
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->hideFromIndex(),
Text::make('Customer','customer_name'),
Text::make('MID','external_mid'),
Text::make('Batch Ref #','batch_reference'),
Text::make('Batch ID','customer_batch_reference'),
Text::make('Batch Date','settlement_date')->sortable(),
Currency::make('Batch Amount','batch_amt'),
Text::make('Funding Reference','payment_reference')->hideFromIndex(),
Text::make('Funding Date','payment_date')->hideFromIndex(),
Currency::make('Funding Amount','fund_amt')->hideFromIndex(),
// **Relationships**
HasMany::make('Transactions'),
BelongsTo::make('Merchant')->hideFromIndex(),
// ***
];
}
/**
* Get the cards available for the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function cards(Request $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function filters(Request $request)
{
return [
];
}
/**
* Get the lenses available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function lenses(Request $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function actions(Request $request)
{
return [];
}
}
In Laravel Nova you can modify the result query of any Resource by adding the index Query method. This method allows you to use Eloquent to modify the results with any condition you define.
I understand you just need to maintain the $model property with the model with the default definition and modify the results in the indexQuery method:
...
public static $model = \App\Batch::class;
public static function indexQuery(NovaRequest $request, $query)
{
// Using the same logic of the example above. I recommend to use the $request variable to access data instead of the $_SERVER global variable.
preg_match('/mid\=([0-9]{10})/', $_SERVER['HTTP_REFERER'], $matches);
if (isset($matches['1'])&&$matches['1']!=''){
return $query->where('external_mid', '=', $matches['1']);
}else{
return $query;
}
}
...
About the use of the PHP Global Variable, I recommend you to use the laravel default request() to look into your URL. You can use something like this $request->mid to read the value from the mid value in the URL.
I'm trying to send a notification with a mention of a user in a general channel. This is what I have:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class WeeklyTasksResponsible extends Notification
{
use Queueable;
protected $employee;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(\App\Employee $employee)
{
$this->employee = $employee;
}
/**
* 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 SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('Reponsible for this week is: ' . $this->employee->slack_name);
}
}
This will sent a weekly notification in the general slack channel of our company. The message is "Responsible for this week is: nameofuser". The problem is the user doesn't see a notification of this.
I've also tried do this:
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('Reponsible for this week is: #' . $this->employee->slack_name);
}
But it isn't the same as mentioning someone myself in the channel.
How can I do this?
As mentioned by #HCK you can enable matching of usernames for #username mentions in chat.postMessages by setting the optional parameter link_names to true.
However, creating mentions with usernames is deprecated and should no longer be used.
The recommended approach is to create mentions with the user ID, which will work by default.
Example:
<#U12345678>
See the post A lingering farewell to the username from Slack for details about username deprecation.
I just found the following in the Laravel 6.18.0 source code:
/**
* Find and link channel names and usernames.
*
* #return $this
*/
public function linkNames()
{
$this->linkNames = 1;
return $this;
}
vendor/laravel/slack-notification-channel/src/Messages/SlackMessage.php
So you can use it like this:
public function toSlack($notifiable)
{
return (new SlackMessage)
->linkNames()
...
}
As #erik-kalkoken pointed out, use the Slack user ID, enclosed by <> and with the # sign. You can find it in your profile in the Slack App:
Actually, I tried that way and it's works pretty well.
$content .= "New order!\r\n #person";
return (new SlackMessage)
->content($content)->linkNames();
You would need to add the # before name of person or team and in order for slack to recognize them as mentions not just text, you would need to chain the content with linkNames
When I have a single notifiable user, a single entry in the notifications table is inserted, along with a mail/sms sent which is perfectly working via channels.
The issue is when I have a user collection, a list of 1k users following me, and I post an update. Here is what happens when using the Notifiable trait as suggested for multi-user case:
1k mails/sms sent (issue is not here)
1k notification entries added to the DB's notifications table
It seems that adding 1k notifications to the DB's notifications table is not an optimal solution. Since the toArray data is the same, and everything else in the DB's notifications table is the same for 1k rows, with the only difference being the notifiable_id of the user notifiable_type.
An optimal solution out of the box would be:
Laravel would pick up the fact that it's an array notifiable_type
Save a single notification as notifiable_type user_array or user with notifiable_id 0 (zero would only be used to signify it's a multi notifiable user)
Create/Use another table notifications_read using the notification_id it just created as the foreign_key and insert 1k rows, of just these fields:
notification_id notifiable_id notifiable_type read_at
I am hoping there is already a way to do this as I am at this point in my application and would love to use the built in Notifications and channels for this situation, as I am firing off emails/sms notifications, which is fine to repeat 1k times I think, but it's the entry of the same data into the database that is the problem that needs to be optimized.
Any thoughts/ideas how to proceed in this situation?
Updated 2017-01-14: implemented more correct approach
Quick example:
use Illuminate\Support\Facades\Notification;
use App\Notifications\SomethingCoolHappen;
Route::get('/step1', function () {
// example - my followers
$followers = App\User::all();
// notify them
Notification::send($followers, new SomethingCoolHappen(['arg1' => 1, 'arg2' => 2]));
});
Route::get('/step2', function () {
// my follower
$user = App\User::find(10);
// check unread subnotifications
foreach ($user->unreadSubnotifications as $subnotification) {
var_dump($subnotification->notification->data);
$subnotification->markAsRead();
}
});
How to make it work?
Step 1 - migration - create table (subnotifications)
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSubnotificationsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('subnotifications', function (Blueprint $table) {
// primary key
$table->increments('id')->primary();
// notifications.id
$table->uuid('notification_id');
// notifiable_id and notifiable_type
$table->morphs('notifiable');
// follower - read_at
$table->timestamp('read_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('subnotifications');
}
}
Step 2 - let's create a model for new subnotifications table
<?php
// App\Notifications\Subnotification.php
namespace App\Notifications;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\DatabaseNotification;
use Illuminate\Notifications\DatabaseNotificationCollection;
class Subnotification extends Model
{
// we don't use created_at/updated_at
public $timestamps = false;
// nothing guarded - mass assigment allowed
protected $guarded = [];
// cast read_at as datetime
protected $casts = [
'read_at' => 'datetime',
];
// set up relation to the parent notification
public function notification()
{
return $this->belongsTo(DatabaseNotification::class);
}
/**
* Get the notifiable entity that the notification belongs to.
*/
public function notifiable()
{
return $this->morphTo();
}
/**
* Mark the subnotification as read.
*
* #return void
*/
public function markAsRead()
{
if (is_null($this->read_at)) {
$this->forceFill(['read_at' => $this->freshTimestamp()])->save();
}
}
}
Step 3 - create a custom database notification channel
Updated: using static variable $map to keep first notification id and insert next notifications (with the same data) without creating a record in notifications table
<?php
// App\Channels\SubnotificationsChannel.php
namespace App\Channels;
use Illuminate\Notifications\DatabaseNotification;
use Illuminate\Notifications\Notification;
class SubnotificationsChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
*
* #return void
*/
public function send($notifiable, Notification $notification)
{
static $map = [];
$notificationId = $notification->id;
// get notification data
$data = $this->getData($notifiable, $notification);
// calculate hash
$hash = md5(json_encode($data));
// if hash is not in map - create parent notification record
if (!isset($map[$hash])) {
// create original notification record with empty notifiable_id
DatabaseNotification::create([
'id' => $notificationId,
'type' => get_class($notification),
'notifiable_id' => 0,
'notifiable_type' => get_class($notifiable),
'data' => $data,
'read_at' => null,
]);
$map[$hash] = $notificationId;
} else {
// otherwise use another/first notification id
$notificationId = $map[$hash];
}
// create subnotification
$notifiable->subnotifications()->create([
'notification_id' => $notificationId,
'read_at' => null
]);
}
/**
* Prepares data
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
*
* #return mixed
*/
public function getData($notifiable, Notification $notification)
{
return $notification->toArray($notifiable);
}
}
Step 4 - create a notification
Updated: now notification supports all channels, not only subnotifications
<?php
// App\Notifications\SomethingCoolHappen.php
namespace App\Notifications;
use App\Channels\SubnotificationsChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class SomethingCoolHappen extends Notification
{
use Queueable;
protected $data;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
/**
* THIS IS A GOOD PLACE FOR DETERMINING NECESSARY CHANNELS
*/
$via = [];
$via[] = SubnotificationsChannel::class;
//$via[] = 'mail';
return $via;
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
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!');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return $this->data;
}
}
Step 5 - helper trait for "followers"
<?php
// App\Notifications\HasSubnotifications.php
namespace App\Notifications;
trait HasSubnotifications
{
/**
* Get the entity's notifications.
*/
public function Subnotifications()
{
return $this->morphMany(Subnotification::class, 'notifiable')
->orderBy('id', 'desc');
}
/**
* Get the entity's read notifications.
*/
public function readSubnotifications()
{
return $this->Subnotifications()
->whereNotNull('read_at');
}
/**
* Get the entity's unread notifications.
*/
public function unreadSubnotifications()
{
return $this->Subnotifications()
->whereNull('read_at');
}
}
Step 6 - update your Users model
Updated: no required followers method
namespace App;
use App\Notifications\HasSubnotifications;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* Adding helpers to followers:
*
* $user->subnotifications - all subnotifications
* $user->unreadSubnotifications - all unread subnotifications
* $user->readSubnotifications - all read subnotifications
*/
use HasSubnotifications;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Yes you are right i guess with the default Notifiable trait, you could create a custom channel.
You can check the Illuminate\Notifications\Channels\DatabaseChannel class for default creation and adopt it to a pivot-table one.
Hope this helps to create a new channel with a pivot table. Also, implement a HasDatabasePivotNotifications trait (or similar name) to your own Notifiable trait.