Laravel Notifications system is a wonderful way to notify users via email or SMS. But a less-known feature is notification driver called “database”, which allows saving the messages inside the application’s DB, and then show them inside the system, as internal message alerts. This article will show you how.

This above is an example of what we’ll create. Whenever a new user registers in the system, database notification will be saved for all administrator users. Administrators will see the notifications immediately on their dashboard, and will be able to mark them as read – one by one, or all of them.

This demo project is based on our Laravel QuickAdminPanel generator (and at the end of the article there will be a link to Github repository), but the code below can be added to any Laravel project.


Step 1. Notifications DB Table

To make the notifications work, we need to create DB table. Run this command:

php artisan notifications:table

It will generate a migration class. And then, this, usual one:

php artisan migrate

Here’s the structure of that notifications table:

Schema::create('notifications', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->string('type');
    $table->morphs('notifiable');
    $table->text('data');
    $table->timestamp('read_at')->nullable();
    $table->timestamps();
});

As you can see, it uses Polymorphic Relations, and uuid field as primary key. Also, field “data” will be the one that will store all notification information, let’s build that.


Step 2. Notification Class

php artisan make:notification NewUserNotification

Then, in new file app/Notifications/NewUserNotification.php:

class NewUserNotification extends Notification
{

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

    public function via($notifiable)
    {
        return ['database'];
    }

    public function toArray($notifiable)
    {
        return [
            'name' => $this->user->name,
            'email' => $this->user->email,
        ];
    }
}

Three things here:

– We inject a user into Constructor, to know which user was registered;
– We specify database as notification channel, in via() method;
– We form the array of data for the notification, in toArray() method – you’re free to put whatever data you want there.

In a minute, I will show you how it all works together, a few more steps.


Step 3. Calling the Notification

Similar to another article Laravel: New User Registration – Seed Example “Dummy” Data, we will use Registered() event and create a Listener class for it.

php artisan make:listener SendNewUserNotification

Then, in new file app/Listeners/SendNewUserNotification.php:

class SendNewUserNotification
{
    public function handle($event)
    {
        $admins = User::whereHas('roles', function ($query) {
                $query->where('id', 1);
            })->get();

        Notification::send($admins, new NewUserNotification($event->user));
    }
}

In other words, we’re firing the notification from the previous step, to all users with Role ID 1 (administrators, in our case).
Notice the parameter $event->user – that it the new registered user.

To actually “fire” that event, we need to add this to app/Providers/EventServiceProvider.php:

use App\Listeners\SendNewUserNotification;
// ...

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
            SendNewUserNotification::class,
        ],
    ];

    // ...
}

Now, with every new registration, a notification will be saved to the database. Like this:


Step 4. Showing Notifications to Administrators

As a homepage, we have a simple HomeController with index() method:

class HomeController
{
    public function index()
    {
        $notifications = auth()->user()->unreadNotifications;

        return view('home', compact('notifications'));
    }
}

See how easy it is to get the notification addressed to a specific user?

$notifications = auth()->user()->unreadNotifications;

And you can show them in Blade however you want, in our resources/views/home.blade.php we have this:

@if(auth()->user()->is_admin)
    @forelse($notifications as $notification)
        <div class="alert alert-success" role="alert">
            [{{ $notification->created_at }}] User {{ $notification->data['name'] }} ({{ $notification->data['email'] }}) has just registered.
            <a href="#" class="float-right mark-as-read" data-id="{{ $notification->id }}">
                Mark as read
            </a>
        </div>

        @if($loop->last)
            <a href="#" id="mark-all">
                Mark all as read
            </a>
        @endif
    @empty
        There are no new notifications
    @endforelse
@endif

See that auth->user->is_admin? It’s an attribute of a model in app/User.php:

class User extends Authenticatable
{
    // ...

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

    public function getIsAdminAttribute()
    {
        return $this->roles()->where('id', 1)->exists();
    }

}

Here’s how it looks:


Step 5. Mark Notifications as Read

Noticed that in the code above we had unreadNotifications as property? How do they define if it was read?

By database column notifications.read_at which is NULL by default, or will contain a timestamp if it was read.

So, to mark notifications as read, we just need to fill in that column. We will to that via AJAX calls from the dashboard, but first let’s define a route and controller for it.

routes/web.php:

// This should be under 'auth' middleware group
Route::post('/mark-as-read', 'HomeController@markNotification')->name('markNotification');

And then the method itself:

public function markNotification(Request $request)
{
    auth()->user()
        ->unreadNotifications
        ->when($request->input('id'), function ($query) use ($request) {
            return $query->where('id', $request->input('id'));
        })
        ->markAsRead();

    return response()->noContent();
}

So we process either id of a particular notification, or all of them together.

As you can see, we don’t need to interact with read_at column directly, we just need to call markAsRead() method!

And, we call this route via AJAX, so in our resources/views/home.blade.php we have this jQuery code:

@if(auth()->user()->is_admin)
    <script>
    function sendMarkRequest(id = null) {
        return $.ajax("{{ route('admin.markNotification') }}", {
            method: 'POST',
            data: {
                _token,
                id
            }
        });
    }
    $(function() {
        $('.mark-as-read').click(function() {
            let request = sendMarkRequest($(this).data('id'));
            request.done(() => {
                $(this).parents('div.alert').remove();
            });
        });
        $('#mark-all').click(function() {
            let request = sendMarkRequest();
            request.done(() => {
                $('div.alert').remove();
            })
        });
    });
    </script>
@endif

That’s it!
Github repository: LaravelDaily/Laravel-Notifications-Database