fbpx

Use Laravel Spark Notifications Without Breaking Laravel’s Default Database Notifications

Laravel Spark Logo

Laravel Spark is a pretty nifty framework that allows you to get up and running quickly with your next software-as-a-service (SaaS) idea. I’ve used it in a couple places, including my leads group management platform called Pond.

One of the many features it includes is a notifications system. The system supports two kinds of notifications:

  • Application-wide notifications to all users of the system
    • Like announcing new features!
  • In-app notifications to individual users when things happen
    • Like “You have a friend request!”

Pretty useful stuff. This is all tracked in the database using an included database migration, creating a table called “notifications”.

The Problem

However, by using this table name, Spark totally railroads Laravel’s existing database notification channel. As soon as you run php artisan notifications:table, you’ll get a nice little error message: A CreateNotificationsTable class already exists.

Oops. And you can change the name of the migration class if you want, but you’ll still get a MySQL error when you actually run the migration. Spark’s notification system just doesn’t play nice with Laravel’s existing database notifications.

So how to fix it?

Edit the Migration

This tutorial assumes you’ve started with Spark, and therefore, we’ll update Laravel’s default database notification system to play nice, rather than try to update the Spark version.

First, create a new migration by calling php artisan make:migration create_nonspark_notifications_table. This will create a new migration in your migrations folder.

Next, find the original migration in your vendor folder: vendor/laravel/framework/src/Illuminate/Notifications/Console/stubs/notifications.stub. Replace the contents of the migration you just created with the contents of this file.

Last thing to make the migration work is to change the name of the table to something other than notifications, since we can’t have two tables with the same name.

public function up()
{
    // Right here, and also in the `down` function.
    Schema::create('nonspark_notifications', function (Blueprint $table) {
        $table->uuid('id')->primary();
        $table->string('type');
        $table->morphs('notifiable');
        $table->text('data');
        $table->timestamp('read_at')->nullable();
        $table->timestamps();
    });
}

Replace the Database Notification Class

The rest of Laravel’s functionality depends on the database notification class, which is where the table is defined. We need to update that as well. Go ahead and create a new class—I put mine in app/Notifications. So it’s easy to spot, call it DatabaseNotification.php. Paste in the following, swapping the table attribute with whatever name you may have used.

This is a full-blown Eloquent model, so there are actually all sorts of use cases for extending this class. One app I was building needed to send a daily digest of notifications via email (a notification of notifications!), so I ended up adding a scope for notifications created in the last 24 hours to this class as well.

<?php 

namespace App\Notifications;

use Illuminate\Notifications\DatabaseNotification as BaseDatabaseNotification;

class DatabaseNotification extends BaseDatabaseNotification
{
    protected $table = 'nonspark_notifications';
}

Create Our Own Notifiable Trait

The Notifiable trait is what allows a model to be, well, notifiable! It’s how you can do stuff like $user->notifications.

Fortunately, despite hogging our database table name, Spark does stick to its own notification channel, so we can still leverage this trait. However, we need to tell it that we’ve replaced the database notification class. So we’ll extend it. Create a new trait in app/Traits called Notifiable.php:

<?php 

namespace App\Traits;

use Illuminate\Notifications\Notifiable as BaseNotifiable;
use App\Notifications\DatabaseNotification;

trait Notifiable {

    use BaseNotifiable;

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

}

We’re still depending on the base Laravel notifiable trait, but we’re swapping out the relationship, so when you call something like $user->notify(new InvoicePaid($invoice));, the new database class (and, therefore, table) will be used.

Now, for all your notifiable models, you’ll simple use this trait instead of the bundled one, keeping all the functionality you know and love while supporting our new table. It’s a drop-in replacement, in case you’ve already been using the default trait:

<?php 

namespace App\Models;

use Laravel\Spark\CanJoinTeams;
use Laravel\Spark\User as SparkUser;

// use Illuminate\Notifications\Notifiable;
use App\Traits\Notifiable;

class User extends SparkUser
{
    use CanJoinTeams,
        Notifiable;

}

And we’re done!

Alternatives

As an alternative to this, you could create a new notification channel with its own table. However, leveraging the existing channel gives you the Eloquent relationship and table storage logic for free.

It may also be possible (easier?) to swap out Spark’s notification class. This wasn’t the route I went, but I have a feeling it could be as simple as changing out a singleton, depending on how it’s been implemented.

Questions? Comments? Other strategies? Let me know in the comments.


Are You a Do-It-Yourself WordPress-er?

If you aren't ready to invest in Sterner Stuff, I've got something for you! Check out my new (and free) WordPress for Small Business Owners email course!

Leave a reply

Your email address will not be published. Required fields are marked *