Laravel Notification doesn't work when queued - laravel

I'm using notifications to send emails to the admin when a new note added by the user. If I don't use the ShouldQueue, all work fine.. When I use queue I got an error
ErrorException: Undefined property: App\Notifications\NewKidNote::$note
what might be the reason?
here is my notification code
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use App\Models\KidNote;
class NewKidNote extends Notification
{
use Queueable;
protected $note;
protected $kidname;
protected $userfullname;
protected $color;
protected $sentnote;
protected $kidid;
public function __construct($note)
{
$this->note = $note;
$this->kidname = $note->kid->name;
$this->userfullname = $note->user->fullname;
$this->color = $note->color;
$this->sentnote = $note->note;
$this->kidid = $note->kid_id;
}
public function via($notifiable)
{
return ['mail','database'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->subject('New Note added')
->greeting('Hello ')
->line('New Note has been added for '. $this->kidname. ' by '.$this->userfullname)
->line('Note Color: '.$this->color)
->line('Note')
->line($this->sentnote)
->action('You can also see the note here', route('admin.children.show',$this->kidid));
}
public function toDatabase($notifiable)
{
return [
'icon' => 'notepad',
'color' => $this->color,
'message' => 'New note added for '. $this->kidname ,
'link' => route('admin.children.show',$this->kidid)
];
}
}

Note to self.. Make sure you clean the cache and restart the queue :)) then it works fine!! This code runs perfectly. thanks

Related

Laravel error when sending email notification

Laravel Version: 8.78.1
PHP Version: 8.0.10
I've created a custom command to run on a schedule and email a notification.
My Command class handle method:
public function handle()
{
$sql = "SELECT * FROM Licences WHERE (Expired = 1)";
$list = DB::select($sql);
return (new NotifyExpiredLicences($list))->toMail('me#gmail.com');
}
My notification method:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Clients with Expired Licences')
->markdown('vendor/notifications/expiredlicences',
['clients' => $this->list, 'toname' => 'Me']);
}
Whenever I test this by running it manually with php artisan email:expired-licences I get the following error Object of class Illuminate\Notifications\Messages\MailMessage could not be converted to int from my command class in the handle method.
However, the preview of my email works fine & displays as expected:
Route::get('/notification', function () {
return (new SendExpiredLicences())->handle();
});
If I remove the return statement from my handle() method, then although I get no errors, neither in my console or in storage\logs, also the preview stops working.
At this point I'm sure I've missed something important from the way this is supposed to be done, but after going through the Laravel docs and looking at online tutorials/examples, I've no idea what.
I've got everything working - though not entirely sure it's the "Laravel way".
If anyone's got suggestions for improving it - add a comment or new answer and I'll try it out.
Console\Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('email:expired-licences')
->weekdays()
->at('08:30');
}
App\Console\Commands\SendExpiredLicences.php:
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences';
protected $description = 'Email a list of expired licences to Admin';
private $mail;
public function _construct()
{
$clients = DB::select("[Insert SQL here]");
$this->mail = (new NotifyExpiredLicences($clients))->toMail('admin#example.com');
parent::__construct();
}
public function handle()
{
Mail::to('admin#example.com')->send($this->mail);
return 0;
}
public function preview()
{
return $this->mail;
}
}
App\Notifications\NotifyExpiredLicences.php:
class NotifyExpiredLicences extends Notification
{
public function __construct(protected $clients)
{
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new Mailable($this->clients));
}
}
App\Mail\ExpiredLicences.php:
class ExpiredLicences extends Mailable
{
public function __construct(private $clients)
{
}
public function build()
{
return $this
->subject('Clients with Expired Licences')
->markdown('emails/expiredlicences',
['clients' => $this->clients, 'toname' => 'Admin']);
}
}
resources\views\emails\expiredlicences.blade.php:
#component('mail::message')
# Hi {!! $toname !!},
#component('mail::table')
| Client | Expired |
| ------------- | --------:|
#foreach ($clients as $client)
|{!! $client->CompanyName !!} | {!! $client->Expired !!}|
#endforeach
#endcomponent
<hr />
Thanks, {!! config('app.name') !!}
#endcomponent
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
return (new SendExpiredLicences())->preview();
});
Ok just to save more commenting, here's what I'd recommend doing. This is all based on the Laravel docs, but there are multiple ways of doing it, including what you've used above. I don't really think of them as "right and wrong," more "common and uncommon."
Console\Kernel.php: I'd keep this mostly as-is, but pass the email to the command from a config file, rather than having it fixed in the command.
use App\Console\Commands\SendExpiredLicences;
…
protected function schedule(Schedule $schedule)
{
$recipient = config('myapp.expired.recipient');
$schedule->command(SendExpiredLicences::class, [$recipient])
->weekdays()
->at('08:30');
}
config/myapp.php:
<?php
return [
'expired' => [
'recipient' => 'admin#example.com',
],
];
App\Console\Commands\SendExpiredLicences.php: update the command to accept the email address as an argument, use on-demand notifications, and get rid of preview() method. Neither the command or the notification need to know about the client list, so don't build it yet.
<?php
namespace App\Console\Commands;
use App\Console\Command;
use App\Notifications\NotifyExpiredLicences;
use Illuminate\Support\Facade\Notification;
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences {recipient}';
protected $description = 'Email a list of expired licences to the given address';
public function handle()
{
$recip = $this->argument('recipient');
Notification::route('email', $recip)->notify(new NotifyExpiredLicences());
}
}
App\Notifications\NotifyExpiredLicences.php: the toMail() method should pass the notifiable object (i.e. the user getting notified) along, because the mailable will be responsible for adding the To address before the thing is sent.
<?php
namespace App\Notifications;
use App\Mail\ExpiredLicenses;
use Illuminate\Notifications\Notification;
class NotifyExpiredLicences extends Notification
{
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new ExpiredLicenses($notifiable));
}
}
App\Mail\ExpiredLicences.php: since the mail message actually needs the list of clients, this is where we build it. We get the recipient here, either from the user's email or the anonymous object.
<?php
namespace App\Mail;
use App\Models\Client;
use Illuminate\Notifications\AnonymousNotifiable;
class ExpiredLicences extends Mailable
{
private $email;
public function __construct(private $notifiable)
{
// this allows the notification to be sent to normal users
// not just on-demand
$this->email = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
}
public function build()
{
// or whatever your object is
$clients = Client::whereHas('licenses', fn($q)=>$q->whereExpired(1));
return $this
->subject('Clients with Expired Licences')
->markdown(
'emails.expiredlicences',
['clients' => $clients, 'toname' => $this->notifiable->name ?? 'Admin']
)
->to($this->email);
}
}
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
// create a dummy AnonymousNotifiable object for preview
$anon = Notification::route('email', 'no#example.com');
return (new ExpiredLicencesNotification())
->toMail($anon);
});

Log table creation using Laravel Events

I want to create a log each time when insertion happens to the files table. That is, everytime when an insertion to the files table happens, an event has to trigger to create a log for it automatically. Log table needs to have 3 columns id, fileid - primary key of files table & logDetails ,where the logDetails column store a msg like 1 file inserted.
i have read this - https://laravel.com/docs/8.x/events and also searched many more , but that couldn't help me to find where should i start. I have created an event page and a listeners page for this. But i don't know what to write in that.The below Controller & model does the insertion to the files table well. And where i need ur help is to code for creating a log for this insertion. Any help is much appreciated.
Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\FileLogs;
use Illuminate\Support\Facades\Validator;
use App\Events\InsertFileLog;
class FileLogController extends Controller
{
public function insert(Request $request) // insertion
{
$validator = Validator::make(
$request->all(),
[
'orderId' => 'required|integer', //id of orders table
'fileId' => 'required|integer', //id of file_type table
'status' => 'required|string'
]
);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$obj = new FileLogs();
$obj->orderId=$request->orderId;
$obj->fileId=$request->fileId;
$obj->status=$request->status;
$obj->save();
//dd($obj->id);
if($obj->id!=''){
InsertFileLog::dispatch($order);
return response()->json(['status'=>'success','StatusCode'=> 200, 'message'=>'Successfully Inserted','data'=>$obj]);
}
else{
return response()->json(['status'=>'Failed','message'=>'Insertion Failed'],400);
}
}
Model
class FileLogs extends Model
{
use HasFactory;
use SoftDeletes;
protected $table='files';
protected $fillable = [
'orderId',
'fileId',
'status'
];
}
Event
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use App\Models\FileLogs;
class InsertFileLog
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $order;
public function __construct(FileLogs $order)
{
$this->order = $order;
}
}
Listener
<?php
namespace App\Listeners;
use App\Events\InsertFileLog;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use App\Models\Logs;
class FileLogListener
{
public function __construct()
{
//
}
public function handle(InsertFileLog $event)
{
//
}
}
EventServiceProvider.php
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
public function boot()
{
//
}
}
The App\Providers\EventServiceProvider offers a way to register Events and Listeners. The key is the Event, the values are one or multiple Listeners. You should update your code to contain the following:
EventServiceProvider
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
InsertFileLog::class => [
// Listeners in this array will be executed when InsertFileLog was fired.
FileLogListener::class
],
];
public function boot()
{
//
}
}
The FileLogListener will only be triggered after the event InsertFileLog was fired. To do that, you could do the following, by adding: InsertFileLog::dispatch($order); line before return success response in your insert function.
FileLogController
class FileLogController extends Controller
{
public function insert(Request $request) // insertion
{
$validator = Validator::make(
$request->all(),
[
'orderId' => 'required|integer', //id of orders table
'fileId' => 'required|integer', //id of file_type table
'status' => 'required|string'
]
);
if ($validator->fails()) {
return response()->json($validator->errors(), 400);
}
$obj = new FileLogs();
$obj->orderId=$request->orderId;
$obj->fileId=$request->fileId;
$obj->status=$request->status;
$obj->save();
//dd($obj->id);
if($obj->id!=''){
InsertFileLog::dispatch($obj);
// alternatively, the event helper can be used.
// event(new InsertFileLog($obj));
return response()->json(['status'=>'success','StatusCode'=> 200, 'message'=>'Successfully Inserted','data'=>$obj]);
}
else{
return response()->json(['status'=>'Failed','message'=>'Insertion Failed'],400);
}
}
}
FileLogListener
<?php
namespace App\Listeners;
use App\Events\InsertFileLog;
use App\Models\Log;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class FileLogListener
{
public function __construct()
{
//
}
public function handle(InsertFileLog $event)
{
// $event->order contains the order.
// In your question, you have request an example of creating an insertion to the Log table
Log::create([
'fileId' => $event->order->fileId,
'logDetails' => '1 file inserted',
]);
}
}
Model observers
Alternatively, Model observers provide a solution without having to manually register events/listeners, as they listen to Eloquent events of the observed model like: creating, created, updated, deleted, etc.

Pass variable from controller to notification in Laravel

I am finding it hard to understand the examples from the docs to the scenario I am having. In my project I have an application form which filled up by the user then admin will update that form once the application is approved, canceled etc.
Now I want to notify the user that her/his application has been approved, canceled etc.
in my controller:
public function update(Request $request, $id)
{
$this->validate($request, [
'status' => 'required'
]);
$requestData = $request->all();
$loanapplication = LoanApplication::findOrFail($id);
$loanapplication->update([
"status" => $request->status,
"admin_notes" => $request->admin_notes,
"date_approval" => $request->date_approved
]);
if($request->notifyBorrower = 'on') {
$user_id = $loanapplication->user_id;
$status = $request->status;
$this->notify(new AdminResponseToApplication($user_id));
}
return redirect()->back()->with('flash_message', 'LoanApplication updated!');
}
In my AdminResponseToApplication.php I like to achieve this
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class AdminResponseToApplication extends Notification implements ShouldQueue
{
use Queueable;
public function __construct()
{
//
}
public function via($notifiable)
{
return ['mail','database'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line(.$user->nameHere. 'your application has been '.$statusHere.'.')
->action('check it out', url('/'))
->subject('Regarding with your loan application')
->line('This is system generated. Do not reply here.');
}
public function toDatabase($notifiable)
{
return [
'user_id' => $user->nameHere,
'status' => $statusHere,
'title' => .$user->nameHere. 'your application has been '.$statusHere.'.',
'url' => '/'
];
}
}
How can I achieve that? Thank you in advance!
Get user object and call function notify() on it. $this->notify() will not work because $this is not an instance of User class.
$user = User::find($user_id);
$user in the $user->notify(new AdminResponseToApplication($data)) function is available in notification class as $notifiable.
You can get any value of that object using $notifiable->name etc.
Remember:
AdminResponseToApplication is a class and you can do anything with it that a php class can.
So you can pass as many variables as you want to AdminResponseToApplication class in constructor and do what you want.
$user->notify(new AdminResponseToApplication($data))
As shown above I am sending a $data object to the class which is available in the constructor.
In the class
class AdminResponseToApplication extends notification implements ShouldQueue{
use Queueable;
public $myData;
public function __construct($data)
{
$this->myData = $data; //now you have a $data copied to $this->myData which
// you can call it using $this->myData in any function of this class.
}
}

Laravel Notification not passing data and getting error "must be an instance of App\\Notifications\\User"

I am trying to use Laravel Notification to send email but getting this error
{
"message": "Type error: Argument 1 passed to App\\Notifications\\UserResetPasswordNotify::__construct() must be an instance of App\\Notifications\\User, instance of Illuminate\\Database\\Eloquent\\Collection given, called in /home/fy3bgmgte060/public_html/svs.com/app/Http/Controllers/Api/LoginController.php on line 143",
"status_code": 500
}
My Controller function
public function resendOTPTest(Request $request)
{
$user = User::where(['mobile' => $request->mobile])->first();
Notification::send($user, new UserResetPasswordNotify($user));
return response()->json(['message' => 'success','data' => 'OTP Sent', 'success' => true], 200);
}
my Notification file
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class UserResetPasswordNotify extends Notification
{
use Queueable;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
$user = $this->user;
return (new MailMessage)
->from('info#test.com')
// ->name('Entrance India')
->subject('New OTP from SVS ')
->markdown('mail.userResetPassword', compact('user'));
}
public function toArray($notifiable)
{
return [
//
];
}
}
this id my User Model
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Order;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'fname','lname', 'email','gender', 'password'
];
protected $hidden = [
'password', 'remember_token',
];
}
while trying to use Laravel Notification to send email but getting above error
But same thing is working for User creation function but it is not working for reset Password function
Where am I wrong?
Include User Class in your Notification Class
You are Injecting User Dependency as Typehint to the Magic Method __Cunstructor into your Notification Class.
You have to make sure Class is available there.
Simply use this in your Notification Class.
use App\User
You need to add
use App\User
in Notification file.
Try using $user->notify(new UserResetPasswordNotify($user))

Why mail laravel not working on the staging server?

I try on the my localhost, it works
But if I try on the staging server, it does not works
My controller like this :
<?php
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderReceivedMail;
...
class PurchaseController
{
...
public function test() {
$order = $this->order_repository->find(416);
$user = $this->user_repository->find(1);
Mail::to($user)->send(new OrderReceivedMail($order, $order->store));
}
}
My mail like this :
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderReceivedMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $order;
public $store;
public function __construct($order, $store)
{
$this->order = $order;
$this->store = $store;
$this->subject('subject');
}
public function build()
{
$mail_company = explode(',',config('app.mail_company'));
// dd($mail_company, $this->order->number, $this->store->name, 'test');
return $this->view('vendor.notifications.mail.email-order',['number'=>$this->order->number, 'store_name' => $this->store->name])->bcc($mail_company);
}
}
I try add this :
dd($mail_company, $this->order->number, $this->store->name, 'test');
on the mail
If in my localhost, the result of dd show
But if in the staging server, the result of dd not show
Seems if the staging server, it does not run this statement :
Mail::to($user)->send(new OrderReceivedMail($order, $order->store));
How can I solve this problem?
open config/mail.php, .env files and set your email driver as mail as bellow,
'driver' => env('MAIL_DRIVER', 'mail'), //you must set it in env file too
then you can send emails like bellow, note that emails.admin.member, is the path to your email template, in the example code, laravel will look for a blade template in the path, resources\views\emails\admin\member.blade.php
Mail::queue('emails.admin.member', $data, function($message) {
$message->subject("A new Member has been Registered" );
$message->from('noreply#mydomain.net', 'Your application title');
$message->to('yourcustomer#yourdomain.com');
});
or
Mail::send('emails.admin.member', $data, function($message) {
$message->subject("A new Member has been Registered" );
$message->from('noreply#mydomain.net', 'Your application title');
$message->to('yourcustomer#yourdomain.com');
});

Resources