Notification don't work on queue but work as Direct - laravel

i have a issue with notifications on laravel, if i send a notification directly without a queue this work as well
this notifiction needs send a email and save in database
i use this to call notify as exemple
$user = \App\User::find(1);
$candidato = \App\CandidatoVaga::where('id_user','=','1')->first();
$user->notify(new \App\Notifications\ConviteVagaCandidato($candidato));
And this is \App\Notifications\ConviteVagaCandidato
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class ConviteVagaCandidato extends Notification implements ShouldQueue
{
use Queueable;
protected $CandidatoVaga;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(\App\CandidatoVaga $CandidatoVaga)
{
$this->CandidatoVaga = $CandidatoVaga;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database','mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->greeting('Olá, '.$this->CandidatoVaga->user->DadosPessoais->nome)
->subject('Convite')
->markdown('email.convite_selecao');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'id_vaga' => $this->CandidatoVaga->id_vaga,
'id_user' => $this->CandidatoVaga->id_user,
'mensagem' => 'Você foi pré selecionado para a vaga',
'tipo' => 'Nova Vaga',
];
}
}
This return a sql error SQLSTATE[42601]: Syntax error: 7 ERRO
but without implements ShouldQueue works as well

One of the differences between the sync queue driver and a real queue driver is how the queued job handles stored Models.
Since the sync queue driver is processing the jobs directly in the same process, there is no extra work being done. If you build your Notification with a Model, it uses that exact model instance.
However, when using a real queue, Laravel has to serialize the data stored on the notification for the queue worker to handle it. Because models cannot be serialized easily, what it actually does is just store the model key on the notification, and then when the queue worker processes that job, it re-retrieves that model from the database using the stored key.
Based on the query you mentioned in the comments, it looks to me like your \App\CandidatoVaga model does not have a primary key ($primaryKey is empty). Because of this, there is no primary key field to query ("candidatos_vaga".""), and there is no primary key value stored (is null).
I see you've already come up with a solution for yourself. If, however, you still wanted to attempt to just use the model, you can try this:
Override the getQueueableId() method on your model. By default, this returns the primary key field. But, since you don't have one defined, you would need to override this method to provide some unique data that can be used to find your record again.
Override the newQueryForRestoration() method on your model. By default, this builds a query using the primary key field. But, since you don't have one defined, you would need to override this method to generate a query using the data generated by the getQueueableId() method.
NB: this is untested. I have never done this; this is just what I see looking through the source code.

I was able to solve it in a palliative way
public $id_vaga;
public $id_user;
public $nome;
public $titulo_vaga;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($CandidatoVaga)
{
$this->id_vaga = $CandidatoVaga->id_vaga;
$this->id_user = $CandidatoVaga->id_user;
$this->nome = $CandidatoVaga->user->DadosPessoais->nome;
$this->titulo_vaga = $CandidatoVaga->vaga->titulo_vaga;
}

Related

How to perform action globally when record get updated in laravel

Is that possible to run this job UpdateRateZone globally wheneven Consignment Model gets updated anywhere in the system?
One method is to use Observer but observer doesn't work when update multiple reccord at once like
Consignment::where('status',1)->update(['address'=>'This']);
Is there anything else we can do?
As per laravel docs :
When issuing a mass update via Eloquent, the saving, saved, updating,
and updated model events will not be fired for the updated models.
This is because the models are never actually retrieved when issuing a
mass update.
Laravel does not fire updated event in case of mass update, so its not possible as per my knowledge. Other way is to do manually.
Other than observers there is methods such using closures for events and Registering events manually but all these methods would work if only laravel trigger an event on mass updation .
Yes you can create a Event Listener for your model. You can read up on more info here
In short first you need to create an Event for the needed model, so if its a Updated event create something like this.
php artisan make:event Consignment/Updated
In that file add
class Updated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* #var Consignment
*/
public $consignment;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Consignment $consignment)
{
$this->consignment= $consignment;
}
/**
* Get the event consignment property
*
* #return Consignment
*/
public function getConsignment()
{
return $this->consignment;
}
}
Now you must create a Listener for this event
php artisan make:listener Consignment/UpdatedEvent
And in the handle function add youre logic
/**
* Handle the event.
*
* #param Updated $event
* #return void
*/
public function handle(Updated $event)
{
//
}
And all that is left after that is to register the events for you Model and you do that in your Consignment.php class where you add
/**
* The event map for the category.
*
* #var array
*/
protected $dispatchesEvents = [
'updated' => Updated::class,
];
When you do this:
Model::where('status',1)->update([some stuff]);
Query Builder's update() method is executed instead of Eloquent's update() method.
If you want to trigger Eloquent events, you need to update rows one by one.
You can fire these events manually, but it's tricky and it's a bad idea.
You could just run the UpdateRateZone job manually for all the Consignments that were updated

Laravel 8 Jestream Notifiable Sending email to non existing user does not work

I am using Laravel 8 Jetstream.
Given that I am using an additional and external database, I need to send email notifications to users, whose info is in that external database, where the email column name is different; let's say "electronicmail".
In the official docs it says:
Remember, you may use the Notifiable trait on any of your models. You
are not limited to only including it on your User model.
So I got this external model:
ExternalUser.php
<?php
namespace App\Models\ExternalDatabase;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class ExternalUser extends Model
{
use Notifiable;
/**
* External DB connection
*
* #var string
*/
protected $connection='externalissima';
/**
* The table associated with the model.
*
* #var string
*/
protected $table='External-users';
/**
* Indicates whether or not the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
}
That external External-users table does not have the email column, but as electronicmail.
So, whenever I use:
$ext_user->notify(new \App\Notifications\Test());
The \App\Notifications\Test() notification class is not working, since I guess because there's no exact email column name.
Then how do I customize the email column to make it work?
After hours of searching, I have read also in the same docs that you can make use of on-demand notifications like so:
Route::get('notification', function (){
Auth()->user()->notify(new \App\Notifications\Test()); //THIS IS WORKING
Illuminate\Support\Facades\Notification::route('email',
['tester#mail.com','John Doe']
)->notify(new \App\Notifications\Test()); //THIS, what I need, IS NOT WORKING, why???
});
And my test notification looks like:
Notifications/Test.php
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class Test extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* 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)
->subject('Test notification')
->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 toArray($notifiable)
{
return [
//
];
}
}
In my case, the notifications for the models/User.php are working fine.
However,
Illuminate\Support\Facades\Notification::route('email',
['tester#mail.com','John Doe']
)->notify(new \App\Notifications\Test());
this is not working with on demand notifications with a custom email, why?
How do I fix this? What am I missing?

Laravel run code when new user registrated with default registration code

Im running a laravel 6.9 application with default authentication/registration.
I want to maintain the default registration process but i want to run a curl command if a user has registered.
Is it possible to hook into the default registration process and extend it with extra code?
Observer is good point in code where you can, well, observe if user is just registered but good place to put additional code after user has been registered is event/listener group. There is already Registered event set in EventServiceProvider so you would need to put additional listener beside one already set there (for sending email to newly registered user if opted). To have all sorted next steps should be followed (disclaimer: I am taking that you use all default auth code so far):
First copy registered(Request $request, $user) method from Illuminate\Foundation\Auth\RegistersUsers.php trait to default App\Http\Controllers\Auth\RegisterController
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
//
}
So you would override that piece of default code which is meant to stay intact (as should every code from vendor directory).
Then, you would need to create listener. In App\Providers\EventServiceProvider::listen array, add one more class into value array so it should looks like
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
\App\Listeners\FooBarBaz::class,
],
];
Don't bother for not having created class already, next artisan command will do that for you:
php artisan event:generate
Now, in \App\Listeners\FooBarBaz::class you can make your custom code related to new user:
namespace App\Listeners;
use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class FooBarBaz
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param Registered $event
* #return void
*/
public function handle(Registered $event)
{
// $event->user is accessible here
//
// this method should return void, it is just executed
}
}
Now, back to registered method of RegisterController. Here you would need to initiate event:
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
event(new \Illuminate\Auth\Events\Registered($user));
}
And you are done.
I wanted to show you use of already lot of prebuilt code although Observer is also good place. But also for calling event; I wouldn't put more code than this event(new \Illuminate\Auth\Events\Registered($user)); line into UserObserver::created(User $user). Whole part with event/listener is very good and decoupled now. Of course, you can make even custom event not using default Illuminate's one, just set that new key => value into EventServiceProvider.
Events
Observers

Laravel. How to get id of database notification?

I use database notifications, in notification code I have method toDatabase:
public function toDatabase($notifiable)
{
$user = \App\SomeUsers::where('id', $notifiable->id)->first();
return [
'message' => $message,
];
}
it returns data array which is being sent to database channel mentioned in via method of current notification:
public function via($notifiable)
{
return ['database'];
}
Everything is as usual, BUT... The problem is I need id of notification in database here in current notification file so that I could broadcast message (from current notification file) to frontend which contains id of notificaion in db (so I could somehow identify it to mark as read). How to get it?
P.S. Moreover, database notification may be queueable, so... it seems that I can't get id...
P.P.S Another words I need broadcast message which contains ["id" => "id of just added corresponding database notification"].
<?php
namespace App\Notifications;
use App\Channels\SocketChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Redis;
class MyCustomNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($param)
{
$this->param = $param;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
$channels = ['database'];
return $channels;
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toDatabase($notifiable)
{
info("This is the current notification ID, it's generated right here before inserting to database");
info($this->id);
return [
'id' => **$this->id**,
'message' => 'Notification message',
];
}
}
$this->id solves the problem.
https://laracasts.com/discuss/channels/laravel/get-database-notification-id-in-push-notification
P.S. I want to draw attention to one fact. When I posted this question, I knew about $this->id, but I couldn't make it work. The reason was: when I dive deeper to my target code from the top level I made changes to code, but they didn't apply. The reason is queues. You need to restart laravel worker to apply settings as Laravel caches logic or you need temporarily delete those: implements ShouldQueue and use Queueable.
In order to retrieve the actual ID of the notifications table in Laravel, you need to cast the ID column to string. First, you need to create a new model called, Notification.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Notification extends Model
{
/**
* Cast variables to specified data types
*
* #var array
*/
protected $casts = [
'data' => 'array',
'id' => 'string'
];
}
This way, if you retrieve the model, it will give you the actual ID of the table.
{
"id": "212829d6-5579-449f-a8e5-e86f0a08e0f9",
"type": "App\\Notifications\\CronFailureNotification",
....
}
The accepted answer did not provide a solution for me in Laravel 8. To properly get the id of the notification, listen for the NotificationSent event and retrieve the id there. Example code:
EventServiceProvider.php
protected $listen = [
NotificationSent::class => [
DispatchBroadcastNotification::class
]
];
DispatchBroadcastNotification.php
<?php
namespace App\Listeners;
use App\Notifications\BroadcastNotification;
use Illuminate\Notifications\Events\NotificationSent;
class DispatchBroadcastNotification
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param NotificationSent $event
* #return void
*/
public function handle(NotificationSent $event)
{
$event->notifiable->notify(
new BroadcastNotification($event->notification->id)
);
}
}
BroadcastNotification.php
<?php
namespace App\Notifications;
use App\Models\Tenant\User;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class BroadcastNotification extends Notification implements ShouldBroadcast
{
public $notificationId;
public function __construct($notificationId)
{
$this->notificationId = $notificationId;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via(User $notifiable): array
{
return ['broadcast'];
}
public function toBroadcast(User $notifiable)
{
return new BroadcastMessage([
'notificationId' => $this->notificationId
]);
}
}

Laravel 5.4 saving models via async queue

So I am trying to optimize my site and on every page load and exit I save a metric (time on page, ip address etc) for analytics. However these are decent sized bottlenecks on my server. When viewing the time it takes for things to run my entire function takes ~1-2ms and then saving to the DB takes ~100-200ms. So my goal is to run my function and then dispatch a new job, that will do the actual saving of the metric. This way all of the saving of my models can be offloaded to a queue. Below is a copy of my job
class SaveMetric implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* #return void
*/
public function handle(Metrics $metric)
{
//
$metric->save();
}
}
Then in my controller function after I grab all the values I need I run this
dispatch(new SaveMetric($newMetric));
This seems to run but does not seem to do anything. Am I missing something? (Edit) This does ~something~ it just saves a record to the DB with null in all the fields, as if I created a new metric without any values.
Is it required to pass a queue into the job dispatch?
Do I need to run a daemon or something similar to actually process the things in the queue?
I created the job using the artisan make:job command
You're pretty close.
class SaveMetric implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $metric;
/**
* Create a new job instance.
*
* #param Metrics $metric
*/
public function __construct(Metrics $metric)
{
$this->metric = $metric;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$this->metric->save();
}
}
According to the docs:
In this example, note that we were able to pass an Eloquent model directly into the queued job's constructor. Because of the SerializesModels trait that the job is using, Eloquent models will be gracefully serialized and unserialized when the job is processing. If your queued job accepts an Eloquent model in its constructor, only the identifier for the model will be serialized onto the queue. When the job is actually handled, the queue system will automatically re-retrieve the full model instance from the database.

Resources