05
Oct '19

We’ve all been there. Customer after customer calls saying they haven’t received the email notifications they were expecting from a system. The issues are pretty much always an incorrect address or an over-enthusiastic spam filter, but even so, I decided to start logging all the notifications sent from my applications. If I can track dates, times, senders, and recipients then I’ve got all the evidence I need if there is a query.

Starting with a table for the logs, allowing for tracking of the notifiable record, the recipient user and the sender:

Schema::create('log_notifications', function ($table) {
    $table->increments('id')->unsigned();
    $table->integer('parent_id');
    $table->string('parent_type');
    $table->integer('user_id')->unsigned()->index();
    $table->integer('sender_id')->unsigned()->index();
    $table->string('email');
    $table->string('type');
    $table->timestamps();

    $table->index(['parent_id', 'parent_type']);
});

Then the main code is a trait that can be used by all notifiable models:

trait Notifiable {

    use \Illuminate\Notifications\Notifiable {
        notify as coreNotify;
    }

    /**
     * Log notification relationship
     */
    public function logNotifications()
    {
        return $this->morphMany('\App\Model\LogNotification', 'parent');
    }

    /**
     * Get Sender ID attribute
     * @param  mixed  $value Original value
     * @return integer
     */
    public function getNotifiableSenderIdAttribute($value) {
        if($value)
            return $value;

        return \Auth::check() ? \Auth::user()->id : 0;
    }

    /**
     * Get User ID attribute
     * @param  mixed  $value Original value
     * @return integer
     */
    public function getNotifiableUserIdAttribute($value) {
        if($value)
            return $value;

        if(in_array(class_basename(get_called_class()), ['User']))
            return $this->id;

        return \Auth::check() ? \Auth::user()->id : 0;
    }

    /**
     * Send the given notification
     * @param  mixed  $notification
     * @return void
     */
    public function notify($instance)
    {
        $this->logNotifications()->create([
            'user_id' => $this->notifiable_user_id,
            'sender_id' => $this->notifiable_sender_id,
            'email' => $this->email,
            'type' => get_class($notification)
        ]);

        $this->coreNotify($notification);
    }
}

This assumes that you have made a model for the notification logs at ‘\App\Model\LogNotification’, but you can change that to however your architecture is set up.

It also assumes a ‘User’ model class that contains your application users. It may be that it’s only your User model that will be notifiable, in which case you can happily drop the polymorphic parent record as it should always match your user_id.

And there you go. not the most eath-shattering of scripts, but definitely a good one to help ensure your notifications are behaving themselves.