Preface

A few days ago I was working on a Laravel project that used the user notification system.

The default notification system is truly wonderful, huge props to the authors. I had a unique use case though, where I needed to add an accessor to get some data from the model for a JSON response. The problem was there is no real easy way to do this without overriding some trait methods.

In order to understand what I am doing I need to describe how the notification system works, so bear with me.


The Notification System

To do this, you use the "notifiable" trait on the User model. this adds some helpful methods, as well as the functions needed to create the relationship between the user and the notifications.

The relationship with the database notifications is in the "HasDatabaseNotifications" trait, and references the "DatabaseNotification" class.

This is fantastic, it takes care of everything you could need for you. But... What if you need to add your own tools to the relationship? In that case you might need to create a new "Notifications" model that extends the "DatabaseNotification" class.

<?php

namespace App;

use Illuminate\Notifications\DatabaseNotification;

class Notification extends DatabaseNotification {
    // Your custom methods
}

You would also need to override the "notifications()" method in your users model.

public function notifications()
{
    return $this->morphMany(Notification::class, 'notifiable')
        ->orderBy('created_at', 'desc');
}

Now you can extend the default notifications class to your hearts content.

This seems a bit un-necessary though. It feels like creating a variable that is only ever used once. Look at this code for example:

$message = "hello world!";
echo $message;

Why would you need that variable, when you could just do this?

echo "hello world!";

Same applies here. We dont need a whole class if we are just going to use it once, so I wonder if there might be an easier way? (spoiler, there is!)


Anonymous Classes Have Names

I was looking at the php.net article recently, and discovered that the "get_class()" function happens to work on them. It will look something like this:

echo get_class(new class {});
// class@anonymousphp shell code0x7f403a6720f2

So "Anonymous" classes are not entirely anonymous, they have names. They are just "hidden." Take this code for example:

$className = get_class(new class {});
new $className; // totally works

Can I use this?

This got me thinking. Any relationship function requires the class name to be passed, and "get_class()" returns the class name even if the class is anonymous. I had a terrible idea, that I thought just might work. I put this in my User model:

public function notifications()
{
    return $this->morphMany(
        get_class(
            new class extends DatabaseNotification
            {
                protected $appends = [
                    'decoded_type'
                ];

                public function getDecodedTypeAttribute()
                {
                    // some accessor code
                }
            }
        ),
        'notifiable'
    )
        ->orderBy('created_at', 'desc');
}

Wouldn't you know it, it works! I am absolutely going to hell for doing this, but sometimes its fun to live on the edge.