laravel notification does not work for me - laravel

I want to send an email to user with a message as below:
MessageController.php:
public function store(Request $request)
{
$user_id = auth()->user()->id;
$message = new Message;
$message->title = $request->title;
$message->body = $request->body;
$message->offer_id = $request->offer_id;
$message->user_id = $user_id;
$message->with_profile = $request->employeeProfile;
$message->save();
//return response()->json($message); -> this gives correct message
$offer = Offer::where('id', '=', $request->offer_id)->first();
//here I'm trying to get user Id, basing on offer (I knot I should use other way, but I'll correct it later
$user = User::where('id', '=', $offer->user_id)->first();
$user->notify(new OfferMessage($message)); -> this gives error "Undefined variable: message"
return response()->json(['created' => true], 201);
}
The problem is that it gives me an error: "Undefined variable: message", while I'm reciving correct message when I uncoment "return response()->json($message);"
What am I doing wrong here?
edit:
My Message.php class: (I don't have OfferMessage class)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use App\Notifications\OfferMessage;
class Message extends Model
{
use Notifiable;
}
edit2: Notifications/OfferMessage.php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use App\Message;
class OfferMessage extends Notification
{
use Queueable;
public $message;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
/**
* 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('You've got new message: '.$message->offer_id)
->line($message->title)
->line($message->body)
->line('Sent by:'.$message->user_id)
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

Change your OfferMessage Class toMail method
Change $message TO $this->message
public function toMail($notifiable)
{
return (new MailMessage)
->line("You've got new message: ".$this->message->offer_id)
->line($this->message->title)
->line($this->message->body)
->line('Sent by:'.$this->message->user_id)
->line('Thank you for using our application!');
}

Related

Laravel Echo.js not receiving notification from pusher no log message

I followed up some tutorials about echo and pusher. I configured all the project as necessary but object notifications not appear at browser. I uncoment the line
<?php
namespace App\Notifications;
use App\Models\Admin;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class RegisterNewNotification extends Notification implements ShouldBroadcast
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public $message;
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['broadcast'];
}
/**
* 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', 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' => $this->message,
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'message' => $this->message,
]);
}
public function toArray($notifiable)
{
return [
//
];
}
}
Broadcast::channel('App.Models.Admin.{adminId}', function ($admin, $adminId) {
return $admin->id === $adminId;
});
Route::get('noti',function(){
$user = Admin::first();
$user->notify(new RegisterNewNotification("Hello"));
});
<script type="module">
Echo.private('App.Models.Admin.1')
.notification((notification) => {
console.log(notification.message); // no log
});
//Echo.channel('events').listen('NewUserRegister', (e) => console.log("RealTimeEventMessage: "+e.message));
</script>
enter image description here
I followed up some tutorials about echo and pusher. I configured all the project as necessary but object notifications not appear at browser. I uncoment the line

How to send only one event in Pusher channel and then for database notification store for all users

I've created a broadcast channel App.Models.Admin.Notify where all the admin users will be subscribed to this channel by default.
<?php
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('App.Models.Admin.Notify', function ($m) {
return !is_null($m) && $m->getMorphClass() === 'admin';
});
And this is the Notifications/Base.php where i've set ['database', 'broadcast', OneSignal::class] as notification channels
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use NotificationChannels\OneSignal\OneSignalMessage;
use App\Channels\OneSignal;
abstract class Base extends Notification implements ShouldBroadcast, ShouldQueue
{
use Queueable;
public $model;
protected string $icon;
public function __construct(mixed $m = null)
{
$this->model = $m;
}
/**
* Get notify id
*
* #return string
*/
protected function getNotifyId(): string
{
return $this->id;
}
/**
* Get icon path
*
* #return string
*/
protected function getIcon(): string
{
return asset('favicon.ico');
}
/**
* Get notification link
*
* #return string
*/
protected function getLink(): string
{
return asset('favicon.ico');
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return \App\Channels\PusherBeams\PusherMessage
*/
public function via($notifiable)
{
return [
'database',
'broadcast',
OneSignal::class
];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return array_merge(
[
'link' => $this->getLink(),
'icon' => $this->getIcon(),
],
$this->getData($notifiable)
);
}
/**
* Get data of message.
*
* #param mixed $notifiable
* #return array
*/
protected function getData($notifiable)
{
return [
'title' => 'hello',
'body' => 'world'
];
}
/**
* Create onesignal message of Web
* #param mixed $notifiable
* #return \NotificationChannels\OneSignal\OneSignalMessage
*/
public function toWeb($notifiable)
{
$data = $this->toArray($notifiable);
return OneSignalMessage::create()
->setSubject($data['title'])
->setBody($data['body'])
->setUrl($data['link']);
}
/**
* Create onesignal message of Android
* #param mixed $notifiable
* #return \NotificationChannels\OneSignal\OneSignalMessage
*/
public function toAndroid($notifiable)
{
$data = $this->toArray($notifiable);
return OneSignalMessage::create()
->setSubject($data['title'])
->setBody($data['body'])
->setData('url', str_replace('https://', 'gamepts://', $data['link']));
}
/**
* Websocket message
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\BroadcastMessage
*/
public function toBroadcast($notifiable)
{
return (new BroadcastMessage($this->toArray($notifiable)))->onQueue('pusher');
}
}
So this is the basic setup for my Notifications and broadcasting.
I've a job which runs every minutes and if any reports are found then it should send the notification if any reports are found.
<?php
namespace App\Console\Commands;
use Carbon\Carbon;
use App\Models\MerchantDeposit;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
class CheckCashIn extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'check:cashin';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Check expiration of cash in orders';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$setting = app(\App\Settings\CurrencySetting::class)->currency;
$expired_limit = app(\App\Settings\AdminSetting::class)->expired_payin_limit_notify;
$reports = [];
foreach ($setting as $currency => $s) {
$expired_minutes = $s['expired_minutes'];
$k = MerchantDeposit::where('status', MerchantDeposit::STATUS['PENDING'])
->where('currency', $currency)
->where('created_at', '<=', Carbon::now()->subMinutes($expired_minutes))
->update(['status' => MerchantDeposit::STATUS['EXPIRED']]);
$o = MerchantDeposit::where('merchant_deposits.status', MerchantDeposit::STATUS['EXPIRED'])
->join('reseller_bank_cards', 'reseller_bank_cards.id', 'merchant_deposits.reseller_bank_card_id')
->join('resellers', 'resellers.id', 'reseller_bank_cards.reseller_id')
->where('merchant_deposits.currency', $currency)
->where('merchant_deposits.created_at', '<=', Carbon::now()->subMinutes($expired_minutes))
->having(DB::raw('COUNT(resellers.name)'), '>=', $expired_limit)
->select('resellers.name', DB::raw('COUNT(resellers.name) AS total_expired'), DB::raw('TRUNCATE(SUM(merchant_deposits.amount), 2) AS total_amount'), 'merchant_deposits.currency')
->groupBy('resellers.name', 'merchant_deposits.currency')
->get();
if (!empty($o->toArray()) && $k > 0) {
$reports[$currency] = [];
foreach ($o as $k => $v) {
$reports[$currency][$v->name] = $v->total_expired;
$reports[$currency]['Total Amount'] = $v->total_amount;
}
}
}
if (!empty($reports)) {
Notification::send(\App\Models\Admin::all(), new \App\Notifications\DepositExpiredReport($reports));
}
}
}
And in Model/Admin.php i've set receivesBroadcastNotificationsOn to update the channel name for the broadcast to send into pusher.
<?php
namespace App\Models;
use App\Trait\HasJWTSubject;
use App\Trait\UserLogsActivity;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Hash;
use Illuminate\Notifications\Notifiable;
use Laravel\Lumen\Auth\Authorizable;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Spatie\Permission\Traits\HasRoles;
/**
* Model of admin
* #package Models
*/
class Admin extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
use Authenticatable, Authorizable, HasFactory, HasJWTSubject;
use UserLogsActivity;
use Notifiable;
use HasRoles;
protected $fillable = [
'name',
'username',
'password',
'status',
'timezone'
];
protected $hidden = [
'password',
];
protected $casts = [
'status' => 'boolean',
];
public const STATUS = [
'DISABLED' => false,
'ACTIVE' => true,
];
public function getIsSuperAdminAttribute()
{
return $this->hasRole('Super Admin');
}
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::needsRehash($value) ? Hash::make($value) : $value;
}
public function devices()
{
return $this->morphMany(Device::class, 'user');
}
public function receivesBroadcastNotificationsOn()
{
return 'App.Models.Admin.Notify';
}
}
When the notification is triggered from Notification::send(\App\Models\Admin::all(), new \App\Notifications\DepositExpiredReport($reports)); it will send broadcast notification multiple times to same broadcast channel as shown in the screenshot below
From Notification::send(\App\Models\Admin::all(), new \App\Notifications\DepositExpiredReport($reports)); it is fine to save the notification records for all the admin in database notification but for pusher i want the notification event to sent only once instead of sending multiple times ie 'x' number of admins.
Lets say i have 100 admin users then now pusher will send the event to same channel 100 times. So i want to minimize the pusher event to just 1 channel where all the admin users subscribed can receive the notification but it should also save the records for 100 users in the notification table of database.

Add Multiple OneSignal APP in Custom Notification in laravel

I have two Notification channels
app/Channels/Reseller/Web/OneSignalWeb.php
<?php
namespace App\Channels\Reseller\Web;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use NotificationChannels\OneSignal\Exceptions\CouldNotSendNotification;
use NotificationChannels\OneSignal\OneSignalChannel;
class OneSignalWeb extends OneSignalChannel
{
public function __construct()
{
$client = new OneSignalClient(
env("ONESIGNAL_RESELLER_APP_ID"),
env("ONESIGNAL_RESELLER_REST_API_KEY"),
''
);
parent::__construct($client);
}
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
*
* #return \Psr\Http\Message\ResponseInterface
* #throws \NotificationChannels\OneSignal\Exceptions\CouldNotSendNotification
*/
public function send($notifiable, Notification $notification)
{
if (!$userIds = $notifiable->devices()->where('platform', 'web')->pluck('uuid')->toArray()) {
return;
}
/** #var ResponseInterface $response */
$response = $this->oneSignal->sendNotificationCustom(
$this->payload($notifiable, $notification, $userIds)
);
if ($response->getStatusCode() !== 200) {
throw CouldNotSendNotification::serviceRespondedWithAnError($response);
}
return $response;
}
}
app/Channels/Merchant/Web/OneSignalWeb.php
<?php
namespace App\Channels\Merchant\Web;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use NotificationChannels\OneSignal\Exceptions\CouldNotSendNotification;
use NotificationChannels\OneSignal\OneSignalChannel;
class OneSignalWeb extends OneSignalChannel
{
public function __construct()
{
$client = new OneSignalClient(
env("ONESIGNAL_MERCHANT_APP_ID"),
env("ONESIGNAL_MERCHANT_REST_API_KEY"),
''
);
parent::__construct($client);
}
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
*
* #return \Psr\Http\Message\ResponseInterface
* #throws \NotificationChannels\OneSignal\Exceptions\CouldNotSendNotification
*/
public function send($notifiable, Notification $notification)
{
if (!$userIds = $notifiable->devices()->where('platform', 'web')->pluck('uuid')->toArray()) {
return;
}
/** #var ResponseInterface $response */
$response = $this->oneSignal->sendNotificationCustom(
$this->payload($notifiable, $notification, $userIds)
);
if ($response->getStatusCode() !== 200) {
throw CouldNotSendNotification::serviceRespondedWithAnError($response);
}
return $response;
}
}
In both of these channel only the difference is that in __constructwe load different keys for both Reseller and Merchant
public function __construct()
{
$client = new OneSignalClient(
env("ONESIGNAL_RESELLER_APP_ID"),
env("ONESIGNAL_RESELLER_REST_API_KEY"),
''
);
parent::__construct($client);
}
And this is the Nofications/Base.php where i've load both ResellerWeb and MerchantWeb Notification in via methods
public function via($notifiable)
{
return [
'database',
'broadcast',
ResellerWeb::class,
MerchantWeb::class,
];
}
I want to optimize the norification where instead of loading MerchantWeb::class and ResellerWeb::cass i want to create and load a general channel lets say NotificationWeb::class and want to use it for both Reseller and Merchant. And when i use it i will need to switch the env() keys based on for which i use.
How can this be achieved

Laravel send notification as super-admin from admin controller

I have Super-Admin and Admin roles. In Admin view I've added button to request verification email. The problem I'm having is that when Admin clicks the button to receive verification email, the email is from Admin, not Super-Admin.
How to make this to be sent to Admin from Super-Admin, instead of from Admin?
Route:
Route::post('/dashboard/SendEmailVerification', 'AdminDashboardController#SendEmailVerification')->name('dashboard.SendEmailVerification');
In AdminDashboardController:
use App\Notifications\EmailVerification;
use App\User;
.............
public function SendEmailVerification(Request $request){
$user = User::where('email_verification_code', $request->code)
->withoutGlobalScope('active')
->first();
$user->notify(new EmailVerification($user));
return Reply::success('Email sent!');
}
And the notification email:
namespace App\Notifications;
use App\Traits\SmtpSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use App\User;
class EmailVerification extends Notification implements ShouldQueue
{
use Queueable, SmtpSettings;
protected $user;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(User $user)
{
$this->user = $user;
$this->setMailConfigs();
}
/**
* Get the notification's delivery channels.
*t('mail::layout')
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
$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)
->subject('Confirm your email')
->greeting(__('Hello!'))
->line(__('email.emailVerify.text'))
->action('Confirm', getDomainSpecificUrl(route('front.get-email-verification', $this->user->email_verification_code), $this->user->company));
#->line(__('email.thankyouNote'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return $notifiable->toArray();
}
}
You're getting wrong sender address because you never defined who's the sender in your Notification configuration. There are two ways to do this:
First: The first one is a really simple, but non-dynamic solution. In your .env configuration config, add these lines:
MAIL_FROM_NAME="My Name"
MAIL_FROM_ADDRESS=support#example.com
When you configure this, add this in yout config/mail.php configuration:
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'default value if not found in .env'),
'name' => env('MAIL_FROM_NAME', 'default value if not found in .env'),
],
Note: Don't forget to clear your cache, and restart queue when you do this:
Second: This is a more dynamic solution, since you can load the sender email address from your database. In your SendEmailVerification method, you can query up the super-admin user, and pass to the EmailVerification class:
public function SendEmailVerification(Request $request){
$user = User::where('email_verification_code', $request->code)
->withoutGlobalScope('active')
->first();
$superAdminUser = User::where('role', 'super-admin')->first();
$user->notify(new EmailVerification($user, $superAdminUser));
return Reply::success('Email sent!');
}
Then, change your EmailVerification class:
public $user;
public $superUser;
public function __construct(User $user, User $superUser)
{
$this->user = $user;
$this->superUser = $superUser;
$this->setMailConfigs();
}
And in your toMail() method, add another from() method:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Confirm your email')
->from($superAdminUser->email, $superAdminUser->first_name)
->greeting(__('Hello!'))
->line(__('email.emailVerify.text'))
->action('Confirm', getDomainSpecificUrl(route('front.get-email-verification', $this->user->email_verification_code), $this->user->company));
#->line(__('email.thankyouNote'));
}
Note: Also clear your cache and restart your queue.
Hope that this can resolve your issue. Let me know if you have any problems with these solutions.
You can read more about notifications on official documentation.

Laravel undefined variable in Notification

I'm experimenting with laravel and I came across this article.
I followed it exactly but for some reason when sending the mail in the Notification class, he can't find the $user variable I declared in the constructor. When printing it in the constructor it works so the user object is passed correctly, but when I want to access it in the toMail method, it's inexisting for some reason. Anyone know why & how to fix this?
<?php
namespace App\Notifications;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class UserRegisteredSuccessfully extends Notification
{
use Queueable;
/**
* #var User
*/
protected $user;
/**
* Create a new notification instance.
*
* #param User $user
*/
public function __construct(User $user)
{
$this->$user = $user;
// printing here works
}
/**
* 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)
{
// ERROR HERE (Undefined variable: user)
$user = $this->$user;
return (new MailMessage)
->subject('Succesfully created new account')
->greeting(sprintf('Hello %s', $user->username))
->line('You have successfully registered to our system. Please activate your account.')
->action('Click here', route('activate.user', $user->activation_code))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
Register Method:
/**
* Register new user
*
* #param Request $request
* #return User
*/
protected function register(Request $request)
{
$validatedData = $request->validate([
'username' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
try {
$validatedData['password'] = Hash::make(array_get($validatedData, 'password'));
$validatedData['activation_code'] = str_random(30).time();
$user = app(User::class)->create($validatedData);
} catch(\Exception $ex) {
logger()->error($ex);
return redirect()->back()->with('message', 'Unable to create new user.');
}
$user->notify(new UserRegisteredSuccessfully($user));
return redirect()->back()->with('message', 'Successfully created a new account. Please check your email and activate your account.');
}
Thanks in Advance!
You made 2 typos:
In your constructor:
$this->$user = $user;
Should be:
$this->user = $user;
And in the toMail() method:
$user = $this->$user;
Should be:
$user = $this->user;
The reason it is not working, is because you are currently using the value of $user as the variable name, and you are not assigning the value of the User object to $this->user.

Resources