In my controller I have this code.
event(new ExampleEvent(), ['data' => '123']);
the event helper has payload arguments. I'm trying to study If i get the data value using payload. In my listener
class ExampleListener implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param ExampleEvent $event
* #return void
*/
public function handle(ExampleEvent $event)
{
// dd($this->payload);
}
}
I want to get the data value in my payload as what I add it to my controller. I'm searching it in google on how to get the payload but failed to learn it.
If you pass an object as first parameter to the event() function,
all other parameters are ignored.
You can pass your payload to your event class:
event(new ExampleEvent('123')); // handle the payload in the constructor
Listener:
public function handle(ExampleEvent $event)
{
$event->data; // 123
}
OR
You can use a string as event name:
event('ExampleEvent', ['data' => '123']);
Listener:
public function handle($payload) // no type hint!
{
//$payload == '123'
}
Related
I'm able to broadcast a notification to Pusher, but I'm unable to receive the response back in my livewire component.
Here is my Notification class:
<?php
namespace App\Notifications;
use App\Models\Statement;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class StatementCompletedNotification extends Notification implements ShouldQueue, ShouldBroadcast
{
use Queueable;
public $statement;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Statement $statement)
{
$this->statement = $statement;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database', 'broadcast'];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'user_id' => $this->statement->uploadedBy->id,
'statement_id' => $this->statement->id,
'file_name' => $this->statement->original_file_name
];
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('users.' . $this->statement->uploadedBy->id);
}
}
And here is the getListeners() method on my Livewire component. I've tried several different things here, first off I tried the way it's shown in the docs, just by referencing my StatementCompletedNotification in the listener, like so:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},StatementCompletedNotification" => 'refreshNotifications'
];
}
I noticed that in pusher, my event type is listed as Illuminate\Notifications\Events\BroadcastNotificationCreated, and I found this post online, so I tried that method like so:
public function getListeners()
{
return [
"echo-private:users.{$this->user->id},.Illuminate\\Notifications\\Events\\BroadcastNotificationCreated" => 'refreshNotifications'
];
}
Neither way has worked for me.
Here's where I'm attempting to just get something back in my javascript on the client-side:
Echo.private('users.1')
.notification((notification) => {
console.log(notification);
});
I don't get any response and I have no idea what the problem is. I've spent an insane amount of time on this and can't seem to figure it out.
Also, it appears that my notification is being picked up multiple times in the queue:
A little more background, the flow that I have set up right now is basically:
StatementCompleted event gets fired (not queued), there's a listener that handles the StatementCompleted event which is queued, and calls my StatementCompletedNotification class like so:
public function handle($event)
{
$event->statement->uploadedBy->notify(new StatementCompletedNotification($event->statement));
}
As stated in the doc, laravel will not fire an event on mass update/insert/delete.
https://laravel.com/docs/5.8/eloquent#events
It uses the Builder for this and will not fire an event.
Is there a way that I can still fire an event after a mass update for example? I would only need the query Builder to extract the needed info myself ( log purposes).
It is actually possible , but you have to extend the Eloquent builder ,overwrite the update/insert methods and send the event there.
Just been playing around with it... Needs work, but the basic idea is the following :
class Test extends Model
{
protected $guarded = [];
public $dispatchesEvents = [
'saved' => SavedTest::class
];
/**
* INCLUDE this as a trait in your model.
* Overwrite the eloquentBuilder.
*
* #param \Illuminate\Database\Query\Builder $query
* #return \Illuminate\Database\Eloquent\Builder|static
*/
public function newEloquentBuilder($query)
{
return new TestBuilder($query);
}
}
Extend the eloquent builder...
class TestBuilder extends Builder
{
/**
* Update a record in the database and fire event.
*
* #param array $values
* #return int
*/
public function update(array $values)
{
// normal eloquent behavior.
$result =$this->toBase()->update($this->addUpdatedAtColumn($values));
/*
* Fire event.
*/
if($result){
if( $event = Arr::get($this->model->dispatchesEvents,'saved')){
// at the attributes.
$this->model->fill($this->addUpdatedAtColumn($values));
$queryBuilder =$this->toBase();
event(new $event($this->model,$queryBuilder));
}
}
}
public function insert(array $values)
{
// same idea..
}
}
The event class :
class SavedTest
{
use SerializesModels;
public $model;
public $query;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($model,$query =null)
{
$this->model = $model;
$this->query = $query;
}
}
The listener.
class SavedTestEvent
{
/**
* Create the event listener.
*
*
*/
public function __construct()
{
}
/**
* Handle the event.
*
* #param object $event
* #return void
*/
public function handle($event)
{
// The model , with the attributes.
dump($event->model);
// the query builder , you could extract the wheres or whatever to build your own log for it.
dump($event->query);
}
}
#Paolo on batch request it would not be file the event you must have to perform operation on single record.. like
Analytic::where('id', '>', 100)->get()->each(function($analytic) {
$analytic->delete();
});
How one would test email is sent as a final outcome after triggering a notification or doing an action that triggers notification?
Ideally, there is a notification merely for sending an email. My first thought was to trigger it and then check if Mail::assertSent() is sent. However, it appears that this does not work as Notification returns Mailable but does not invoke Mail::send().
Relevant GitHub issue: https://github.com/laravel/framework/issues/27848
My first approach for test:
/** #test */
public function notification_should_send_email()
{
Mail::fake();
Mail::assertNothingSent();
// trigger notification
Notification::route('mail', 'email#example.com')
->notify(new SendEmailNotification());
Mail::assertSent(FakeMailable::class);
}
while the Notification toMail() method looks as:
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\FakeMailable
*/
public function toMail($notifiable)
{
return (new FakeMailable())
->to($notifiable->routes['mail']);
}
The set-up example is available https://github.com/flexchar/laravel_mail_testing_issue
You can use mailCatcher then extends your TestCase
class MailCatcherTestCase extends TestCase
{
protected $mailCatcher;
/**
* MailCatcherTestCase constructor.
* #param $mailCatcher
*/
public function __construct($name = null, array $data = [], $dataName = ''
) {
parent::__construct($name, $data, $dataName);
$this->mailCatcher = new Client(['base_uri' => "http://127.0.0.1:1080"]);
}
protected function removeAllEmails() {
$this->mailCatcher->delete('/messages');
}
protected function getLastEmail() {
$emails = $this->getAllEmail();
$emails[count($emails) - 1];
$emailId = $emails[count($emails) - 1]['id'];
return $this->mailCatcher->get("/messages/{$emailId}.json");
}
protected function assertEmailWasSentTo($recipient, $email) {
$recipients = json_decode(((string)$email->getBody()),
true)['recipients'];
$this->assertContains("<{$recipient}>", $recipients);
}
}
then you can use in you test
/** #test */
public function notification_should_send_email()
{
// trigger notification
Notification::route('mail', 'email#example.com')
->notify(new SendEmailNotification());
$email = $this->getLastEmail();
$this->assertEmailWasSentTo($email, 'email#example.com');
}
since you can fetch the mail, so that you can test mail body, subject, cc, attachment etc.
don't forget to remove all mails in tearDown
hope this helps.
I have a receiveEmail boolean field in the User model of a Laravel application. How do I ensure that mail notifications respect this field, and only sends the email to the user if the field is true?
What I want is that this code:
$event = new SomeEvent($somedata);
Auth::user()->notify($event);
where SomeEvent is a class that extends Notification and implements 'mail' on the via() method, only sends an email if the user has allowed emails.
Have any one checked this via() method:
https://laravel.com/docs/6.x/notifications#specifying-delivery-channels
public function via($notifiable)
{
// $notifiable object is User instance for most cases
$wantsEmail = $notifiable->settings['wants_email']; // your own logic
if(!$wantsEmail){
// no email only database log
return ['database'];
}
return ['database', 'mail'];
}
I hope this will work while sending notifications to multiple users too. Thanks
I ended up creating a new Channel that implements the checking. In app/channels, add your channel, something like this:
namespace App\Channels;
use App\User;
use Illuminate\Notifications\Channels\MailChannel;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Arr;
class UserCheckMailChannel extends MailChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
* #return void
*/
public function send($notifiable, Notification $notification)
{
// check if user should receive emails. Do whatever check you need here.
if ($notifiable instanceof User && !$notifiable->receiveEmails) {
return;
}
// yes, convert to mail and send it
$message = $notification->toMail($notifiable);
if (!$message) {
return;
}
parent::send($notifiable, $notification);
}
}
Then bind your class on Providers/AppServiceProvider.php to the old mail class:
/**
* Register any application services.
*
* #return void
*/
public function register()
$this->app->bind(
\Illuminate\Notifications\Channels\MailChannel::class,
UserCheckMailChannel::class
);
}
try to create new method in user model like this..
user model file..
public function scopeNotifyMail() {
if($this->receiveEmail == true) { //if field is enable email other wise not send..
$event = new SomeEvent($somedata);
$this->notify($event);
}
}
and now call like this in controller..
Auth::user()->notifyMail();
or
App\User::find(1)->notifyMail();
or
App\User::where('id',1)->first()->notifyMail();
I am trying to do pushnotification when a new user signup.So I created events called MemberNotificationEvents, when I fired an event event(new MemberNotificationEvent($UserDetails)); on my signUpController flow is completely going but on the MemberNotificationListener a public function handle(MemberNotificationEvent $event) return error that :
Call to a member function send() on string
I put full code of MemberNotificationListener :
<?php
namespace App\Listeners;
use App\Events\MemberNotificationEvent;
use App\Services\PushNotificationService;
use Illuminate\Contracts\Queue\ShouldQueue;
class MemberNotificationListener implements ShouldQueue
{
private $pushNotificationService;
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
$this->pushNotificationService = PushNotificationService::class;
}
private function getMessageBody($username)
{
return "Awesome! Welcome " . $username . " to IDM";
}
/**
* Handle the event.
*
* #param object $event
* #return void
*/
public function handle(MemberNotificationEvent $event)
{
$username = $event->UserDetails->name;
$message = $this->getMessageBody($username);
$this->pushNotificationService->send($event,['body' => $message]); // throw error
}
}
What is the problem in my code?
The problem is with this line:
$this->pushNotificationService = PushNotificationService::class;
When you do SomeClass::class, it means you supply the class name - not the actual class.
Hence, when you later do $this->pushNotificationService->send(...), the push notification service is just the class name and not the service class.
The second part of the problem is that you need an actual object to put in there. Laravel can inject it for you in the constructor, and then you can supply it. Like this:
public function __construct(PushNotificationService $service)
{
$this->pushNotificationService = $service;
}