Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Notifications Event Listener Undefined Property

I have the following error:

Undefined property: Illuminate\Notifications\Events\NotificationSent::$user in /var/www/app/app/Listeners/NoticationListener.php:31

The error occurs here:

<?php

namespace App\Listeners;

use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class NoticationListener implements ShouldQueue
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  NotificationSent  $event
     * @return void
     */
    public function handle(NotificationSent $event)
    {
       $notification = $event->notifiable;
       $addressee = $notification; //error here
       $address = $notification; 
       $type = "Notification";
       dispatch(new SendEmail($type,$addressee,$address));
    }
}

I don't understand this undefined property, especially on this line. How can I dd() from here? I tried to log the $event but I couldn't get it to log, only got this error.

My notifications work very well in the application, I just want an email to accompany them, which is why I have this event-listener/job.

Thank you.

EDIT

The repository code that is dispatching the notification is below:

public function notify($asset)
{
    $users = User::where("id","!=",Auth::user()->id)->get();
    Notification::send($users, new NewAsset($asset));
}

That extension of the Notification class is below:

class NewAsset extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    protected $asset;

    public function __construct($asset)
    {
        $this->asset = $asset;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['database'];
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            'asset_id' => $this->asset->id
        ];
    }
}

EDIT 2

If someone could advise how to do some error checking at this juncture, that could be helpful going forward. Because the code is async on the server it isn't returning data to the client, and when I try to get it to Log it doesn't seem to do so before it gets caught in the error.

How can I go about debugging in this scenario?

I've looked at the framework's source code and have no idea where the $user property is coming from. I assume it has to do with $event->notifiable being tied to the User model, but if it fires correctly for all affected users from within the application, why would its property be undefined in this context?

Please assist, thank you.

like image 207
Summer Developer Avatar asked Sep 16 '17 22:09

Summer Developer


1 Answers

This is a strange issue. As you found yourself, Laravel doesn't set a $user property on that object itself, so something else must be involved. Here's my understanding of the process:

  1. Notification::send() → enqueue NewAsset notification for each user
  2. Dequeue internal job for NewAsset notification → send notifications
  3. Fire NotificationSent event → enqueue NotificationListener handler

Error seems to occur here:

  1. Dequeue internal job for NotificationListener handler → handle event
  2. Dispatch job for SendEmail [→ enqueue if ShouldQueue]
  3. [Dequeue job for SendEmail if ShouldQueue →] Send notification email

As you can see, there's a lot of serialization and deserialization going on when enqueueing and dequeueing the jobs for the notification. It seems like the framework is trying to set the $user property when deserializing the the NotificationSent event from the queue, but it's hard to tell from your question without a full stack trace, and I'm not sure what adds $user to the serialized data without more visibility into the code.

Here are some debugging suggestions to try:

Restart the queue worker:

After a queue worker starts, the PHP process doesn't reload changes to the source code from disk if the autoloader already imported the file. It seems possible that someone added the $user property to an object but didn't restart the worker, so it never picked up the change. During development, we need to restart the queue workers whenever the code changes for a queueable item. When deploying to production, it's a good idea to restart queue workers as part of the deployment process.

Alternatively, use the artisan queue:listen command instead of queue:work during development. This mode restarts the entire framework for each job.

Set QUEUE_DRIVER to sync:

This prevents the framework's event system from serializing the event data. If the notification email sends without any errors, then we know that custom code somewhere is adding a $user property to the event.

Check serialized queue data:

It's not clear from the question which queue driver your app uses. If you're not using sync, we can look at the pending jobs in the queue to try to find the discrepancy (in the database, Redis, etc).

  1. Stop all queue workers.
  2. Trigger the notification through the app.
  3. Run php artisan queue:work --once to manually process one job until the queued job runs and fires the NotificationSent event.
  4. Inspect the job created in the queue to handle the NotificationSent event

We can also use this approach to dump data using dd() during queue jobs because artisan queue:work --once runs in the foreground.

Don't enqueue the NotificationSent event handler:

Because the notification is already configured to process in a queued background job, we don't necessarily need to enqueue the notification event handler as well. Try removing the ShouldQueue interface to see if this resolves the problem.

As other commenters mentioned, this problem may be better solved using Laravel's Mail Notifications which remove the need for a separate NotificationSent event handler entirely.

like image 194
Cy Rossignol Avatar answered Nov 15 '22 14:11

Cy Rossignol