Laravel 8 Queue Errors Ramsey\Uuid\Uuid::unserialize(string $data): void must be compatible with Serializable::unserialize($serialized) - laravel

I have some notifications (that get sent out to SendGrid) and I am using table queues.
I build my notification as below
$data = [
'email_message' => $message,
'subject' => $request->subject,
'email' => $request->email
];
$user->notify(new EmailGeneralNotification($data));
My Notification looks like this;
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use NotificationChannels\SendGrid\SendGridChannel;
use NotificationChannels\SendGrid\SendGridMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
// use Illuminate\Queue\SerializesModels;
use App\Models\NotificationTemplate;
class EmailGeneralNotification extends Notification implements ShouldQueue
{
use Queueable;
// use SerializesModels;
protected $data;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(array $data) {
$this->data = $data;
}
public function via($notifiable)
{
return [
SendGridChannel::class,
];
}
public function toSendGrid($notifiable)
{
$reference = 'internal_general_email';
$template = NotificationTemplate::where('reference',$reference)->latest('updated_at')->first();
if(!$template)
{
dd("No template was found for " . $reference);
}
return (new SendGridMessage($template->template_id))
/**
* optionally set the from address.
* by default this comes from config/mail.from
* ->from('no-reply#test.com', 'App name')
*/
/**
* optionally set the recipient.
* by default it's $notifiable->email:
* ->to('hello#example.com', 'Mr. Smith')
*/
->payload([
"subject" => (array_key_exists('subject', $this->data)) ? $this->data['subject'] : '',
"email_message" => (array_key_exists('email_message', $this->data)) ? $this->data['email_message'] : '',
]);
}
}
This gets sent via a SendGrid notification type.
The funny this is that when I remove the implements ShouldQueue and use Queueable and run my function everything works fine.
When I enable the queue I get the below;
[2022-10-30 08:34:08] local.ERROR: Declaration of Ramsey\Uuid\Uuid::unserialize(string $data): void must be compatible with Serializable::unserialize($serialized) {"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Declaration of Ramsey\\Uuid\\Uuid::unserialize(string $data): void must be compatible with Serializable::unserialize($serialized) at /var/www/html/vendor/ramsey/uuid/src/Uuid.php:307)
[stacktrace]
#0 {main}
"}
I have updated php to v8 and got Laravel to 8.83.25 but still no joy

Related

Can we send different emails to user and admin at once using single event in laravel?

I am creating a leave request application for employee in an organization where user can create a leave request and admin(s) can approve/reject the request. The user and admin(s) should receive mail each time after making a leave request and after approval too. I am using event listener to send the mail and could send the mail using separate events and listeners(4 events:
making leave request to admin,
making leave request to user,
after accepting/rejecting the request to admin and
accepting/rejecting the request to user
and likewise 4 listeners too)
but it would be better if I could reduce the number of events and listeners in this case since the user mail and email template only are different.
I studied about event subscribers which subscribe to multiple events from within the subscriber class itself which is not what I want.
Is it possible to send multiple emails to multiple user/admin using less number of events/listeners?
I am using laravel 8
Code:
controller is:
public function store(Request $request)
{
DB::beginTransaction();
try{
$request->validate(
[
'leave_type' => 'in:halfday,fullday',
'start_date' => 'required',
'end_date' => 'required',
'assignee' => 'required',
]
);
$datetime1 = new \DateTime($request->start_date);
$datetime2 = new \DateTime($request->end_date);
$interval = $datetime1->diff($datetime2);
$days = $interval->format('%a');//total requested days
$leave_request= new LeaveRequest();
$leave_request->requested_by = auth()->user()->id;
$leave_request->leave_type_id = $request->leave_type_id ;
$leave_request->leave_type = $request->leave_type;
$leave_request->start_date = $request->start_date;
$leave_request->end_date = $request->end_date;
$leave_request->total_days = $days+1;
$leave_request->assignee = $request->assignee;
$leave_request->reviewer = $request->reviewer;
if($leave_request->save()){
$user = User::where('id', auth()->user()->id)->first();
$assignee = User::where('id',$request->assignee)->first();
$assignee->locale = App::currentLocale();
$assignee->requested_by = $user->name;
event(new SendLeaveRequestToAdminEvent($assignee));//send mail to admin asignee
if($request->reviewer !=null){
$reviewer = User::where('id',$request->reviewer)->first();
$reviewer->locale = App::currentLocale();
$reviewer->requested_by = $user->name;
event(new SendLeaveRequestToAdminEvent($reviewer));//send mail to admin reviewer(optional)
}
$user->locale = App::currentLocale();
event(new SendLeaveRequestToUserEvent($user));//send mail to user
DB::commit();
return response()->json([
'message'=> 'Leave requested successfully',
'status_code' => 200,
]);
}
else{
DB::rollback();
return response()->json([
'status_code' => '204',
'message' => 'Leave request failed',
]);
}
} catch (Exception $error) {
DB::rollback();
return response()->json([
'message' => 'Leave request failed',
'error' => $error
]);
}
}
first event to mail to admin(s) i.e; assignee and reviewer after sending leave request is
<?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\User;
class SendLeaveRequestToAdminEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $admin;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $admin)
{
$this->admin = $admin;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
second event to mail to user after sending leave request is
<?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\User;
class SendLeaveRequestToUserEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
first listener to admin(s):
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Mail;
use App\Mail\LeaveRequestAdmin;
use App\Events\SendLeaveRequestToAdminEvent;
class SendLeaveRequestToAdminListener
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param SendLeaveRequestToAdminEvent $event
* #return void
*/
public function handle(SendLeaveRequestToAdminEvent $event)
{
$email = $event->admin->email;
Mail::to($email)->locale($event->admin->locale)->send(new LeaveRequestAdmin($event->admin));
}
}
second listener to user:
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Mail;
use App\Mail\LeaveRequestUser;
use App\Events\SendLeaveRequestToUserEvent;
class SendLeaveRequestToUserListener
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param SendLeaveRequestToUserEvent $event
* #return void
*/
public function handle(SendLeaveRequestToUserEvent $event)
{
$email = $event->user->email;
Mail::to($email)->locale($event->user->locale)->send(new LeaveRequestUser($event->user));
}
}
mail to admin:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\URL;
class LeaveRequestAdmin extends Mailable
{
use Queueable, SerializesModels;
public $admin;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($admin)
{
$this->admin = $admin;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->markdown('email.leaverequestadmin', [
'url' => URL::to('/home'),
'admin' => $this->admin,
])->subject('Incoming Leave Request');
}
}
mail to user:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\URL;
class LeaveRequestUser extends Mailable
{
use Queueable, SerializesModels;
public $user;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($user)
{
$this->user = $user;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->markdown('email.leaverequestuser', [
'url' => URL::to('/home'),
'user' => $this->user,
])->subject('Leave Request Received');
}
}
the above code is used after leave request is created which have two separate events/listeners/mail/ blade templates. Like wise I have similar another two events/listeners/mail/ blade templates each which are used after the leave request is accepted/rejected.

How to passing variable from controller to view queued mail?

I'm trying to pass variable $array from controller to mail blade, but whenever I run queue:listen. It always say failed.
Bellow is my code
In controller I have a variable named $array, I've putting it in dispatch
Controller
$array["view"] = "layouts.mail.order";
$array["subject"] = "Order Created";
$array["from"] = env('MAIL_USERNAME');
$array["data"] = "aaaaaaaaa";
$array["email"] = Auth::user()->email;
OrderEmailJob::dispatch($array);
OrderEmailJob
<?php
namespace App\Jobs;
use App\Mail\OrderMail;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class OrderEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $array;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($array)
{
$this->array = $array;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$email = new OrderMail();
Mail::to($this->array['email'])->send($array);
}
}
and this is code for the mailable
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderMail extends Mailable
{
use Queueable, SerializesModels;
public $array;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($array)
{
$this->array = $array;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view($this->array['view'])
->from($this->array['from'], env('MAIL_FROM_NAME'))
->subject($this->array['subject'])
->with([
'data' => $this->array['data'],
]);
}
}
The result I want is I can use variable $array in view for my mail, because I've to printed out data from $array variable
Sorry about my english, thanks
try like this :
public $mailData;
public function __construct($mailData)
{
$this->mailData = $mailData;
}
public function build()
{
// Array for Blade
$input = array(
'action' => $this->mailData['action'],
'object' => $this->mailData['object'],
);
return $this->view('emails.notification')
->with([
'inputs' => $input,
]);
}
I'm not sure, the answer correct. But you can change the name variable $array to $data and check again. Maybe your variable name is a special case like array keyword

How can I send an email to many recipients in laravel?

I can send emails to a lot of recipients but the problem is I am using a business email which I'm subscribe to my web host, the problem is when I send 1 email to a lot of recipients my web host automatically suspends my email's outgoing which technically I'm having problems with right now, so I tried using mailing list but it won't send emails to my recipients using the Mail(), but it works when sending a mail to mailist-join#domain.com.
Controller:
public function imail($request){
$dataEmail = [
'date' => $request->date,
'time_start' => $request->start,
'time_end' => $request->end,
'duration' => abs(strtotime($request->end) - strtotime($request->start))/(60*60),
'areas' => Purifier::clean($request->areas),
'reason' => Purifier::clean($request->activities)
];
$emails = UserEmail::where('email','!=','')->select('email')->get()->pluck('email');
$subject = 'ADVISORY (' . date("F j, Y",strtotime($request->date)) .')';
foreach ($emails as $email) {
Mail::to($email)
->send(new SendToAll($dataEmail,$subject));
}
}
SendToAll Mail
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendToAll extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
// public $afterCommit = true;
public $data,$subject;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($data,$subject)
{
$this->data = $data;
$this->subject = $subject;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->subject($this->subject)
->from('myemail#domain.com','Me')
->view('pages.imail')
->with('data',$this->data);
;
}
}
I also tried supervisor but to no avail. I'm using Windows Server 2012.
You have to use like below code
Mail::to($email)->cc(['mail1','mail2','mail3'])->send(new SendToAll($dataEmail,$subject));

Laravel queues issues when I pass data from controller to jobs

I'm having issue fixing the queue part for Laravel.
I have a contact form which works perfectly. I submit the form and a mail is being sent to me with the input details from the form.
But the issue is that if you do not use the Laravel Queues then the UX is lacking because of the waiting time before the mail is being sent and after getting the success message.
So now I want to implement the queuing part, but im not doing it right. Im still a noob at it...
Here is my controller
KontaktformController.php
<?php
namespace App\Http\Controllers;
use App\Jobs\SendEmailJob;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use App\Kontaktform;
use Illuminate\Http\Request;
use App\Mail\SendEmailMailable;
class KontaktformController extends Controller
{
public function create()
{
return view('kontakt');
}
public function store(Request $request) {
$this->validate($request, [
'name' => 'required|string',
'mobile' => 'required',
'email' => 'required|email',
'subject' => 'required',
'message' => 'required',
]);
/*
Add mail functionality here.*/
$kontakt = new Kontaktform([
'navn' => $request['name'],
'mobilnr' => $request['mobile'],
'fastnetnr' => $request['landline'],
'mail' => $request['email'],
'emne' => $request['subject'],
'beskrivelse' => $request['message']
]);
$kontakt->save();
//$user['email'] = 'test#test.com';
$job = (new SendEmailJob($request['email']))
->delay(Carbon::now()->addSeconds(3));
dispatch($job);
}
}
SendEmailJob.php
<?php
namespace App\Jobs;
use App\Mail\SendEmailMailable;
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\Mail;
class SendEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $user;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$email = new SendEmailMailable('test#test.dk');
Mail::to('admin#admin.dk')->send($email);
}
}
SendEmailMailable.php
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendEmailMailable extends Mailable
{
use Queueable, SerializesModels;
public $request;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($request)
{
$this->request = $request;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('test#test.dk')->view('mail.kontaktform');
}
}
kontaktform.blad.php
{{$request['email']}}
In your kontaktform.blad.php (which should be named kontaktform.blade.php btw), you expect the data to be an array because you try to fetch the index ['email'] though in your SendEmailJob.php in the method handle() you pass your email as a string.
So what you should change it to the following:
$email = new SendEmailMailable(['email' => 'test#test.dk']);
or do the following in the controller & in the job:
Controller:
$job = (new SendEmailJob($request))
->delay(Carbon::now()->addSeconds(3));
Job:
protected $request;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($request)
{
$this->request = $request;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$mailable = new SendEmailMailable($this->request);
Mail::to('admin#admin.dk')->send($mailable);
}
Now all data from the request is should be available in your blade template.
I found the issue. I didnt pass data from mailable to my blade
public function build()
{
return $this->view( 'mail.kontaktform' )->with( [
'request' => $this->request,
] );
}

Laravel: customize or extend notifications - database model

IMHO, the current Database channel for saving notifications in Laravel is really bad design:
You can't use foreign key cascades on items for cleaning up notifications of a deleted item for example
Searching custom attributes in the data column (casted to Array) is not optimal
How would you go about extending the DatabaseNotification Model in vendor package?
I would like to add columns event_id, question_id, user_id (the user that created the notification) etc... to the default laravel notifications table
How do you override the send function to include more columns?
In:
vendor/laravel/framework/src/Illuminate/Notifications/Channels/DatabaseChannel.php
The code:
class DatabaseChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
* #return \Illuminate\Database\Eloquent\Model
*/
public function send($notifiable, Notification $notification)
{
return $notifiable->routeNotificationFor('database')->create([
'id' => $notification->id,
'type' => get_class($notification),
\\I want to add these
'user_id' => \Auth::user()->id,
'event_id' => $notification->type =='event' ? $notification->id : null,
'question_id' => $notification->type =='question' ? $notification->id : null,
\\End adding new columns
'data' => $this->getData($notifiable, $notification),
'read_at' => null,
]);
}
}
To create a custom Notification Channel:
First, create a Class in App\Notifications for example:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class CustomDbChannel
{
public function send($notifiable, Notification $notification)
{
$data = $notification->toDatabase($notifiable);
return $notifiable->routeNotificationFor('database')->create([
'id' => $notification->id,
//customize here
'answer_id' => $data['answer_id'], //<-- comes from toDatabase() Method below
'user_id'=> \Auth::user()->id,
'type' => get_class($notification),
'data' => $data,
'read_at' => null,
]);
}
}
Second, use this channel in the via method in the Notification class:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use App\Notifications\CustomDbChannel;
class NewAnswerPosted extends Notification
{
private $answer;
public function __construct($answer)
{
$this->answer = $answer;
}
public function via($notifiable)
{
return [CustomDbChannel::class]; //<-- important custom Channel defined here
}
public function toDatabase($notifiable)
{
return [
'type' => 'some data',
'title' => 'other data',
'url' => 'other data',
'answer_id' => $this->answer->id //<-- send the id here
];
}
}
Create and use your own Notification model and Notifiable trait and then use your own Notifiable trait in your (User) models.
App\Notifiable.php:
namespace App;
use Illuminate\Notifications\Notifiable as BaseNotifiable;
trait Notifiable
{
use BaseNotifiable;
/**
* Get the entity's notifications.
*/
public function notifications()
{
return $this->morphMany(Notification::class, 'notifiable')
->orderBy('created_at', 'desc');
}
}
App\Notification.php:
namespace App;
use Illuminate\Notifications\DatabaseNotification;
class Notification extends DatabaseNotification
{
// ...
}
App\User.php:
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
// ...
}
An example for #cweiske response.
If you really need extends the Illuminate\Notifications\Channels\DatabaseChannel not creating a new Channel you can:
Extends the channel:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Channels\DatabaseChannel as BaseDatabaseChannel;
use Illuminate\Notifications\Notification;
class MyDatabaseChannel extends BaseDatabaseChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
* #return \Illuminate\Database\Eloquent\Model
*/
public function send($notifiable, Notification $notification)
{
$adminNotificationId = null;
if (method_exists($notification, 'getAdminNotificationId')) {
$adminNotificationId = $notification->getAdminNotificationId();
}
return $notifiable->routeNotificationFor('database')->create([
'id' => $notification->id,
'type' => get_class($notification),
'data' => $this->getData($notifiable, $notification),
// ** New custom field **
'admin_notification_id' => $adminNotificationId,
'read_at' => null,
]);
}
}
And register the Illuminate\Notifications\Channels\DatabaseChannel on application container again:
app\Providers\AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
Illuminate\Notifications\Channels\DatabaseChannel::class,
App\Notifications\MyDatabaseChannel::class
);
}
}
Now when the Illuminate\Notifications\ChannelManager try createDatabaseDriver will return your registered database driver.
More one option to solve this problem!
Unlike "Bassem El Hachem", I wanted to keep the database keyword in the via() methods.
So in addition to a custom DatabaseChannel, I also wrote my own ChannelManager that returns my own DatabaseChannel in the createDatabaseDriver() method.
In my apps' ServiceProvider::register() method, I overwrote the singleton for the original ChannelManager class to return my custom manager.
I solved a similar problem by customizing notification class:
create the class for this action:
artisan make:notification NewQuestion
inside it:
public function __construct($user,$question)
{
$this->user=$user;
$this->question=$question;
}
...
public function toDatabase($notifiable){
$data=[
'question'=>$this->(array)$this->question->getAttributes(),
'user'=>$this->(array)$this->user->getAttributes()
];
return $data;
}
then you can access proper data in view or controller like this:
#if($notification->type=='App\Notifications\UserRegistered')
New question from {{$notification->data['user']['name']}}
#endif

Resources