Search code examples
phplaravellaravel-8php-8

Laravel - Storing notification settings per user with different channels


I am developing a fairly simple application, where I want my users to be able to subscribe to notifications. So the system should:

  • Send notifications when a certain event they are subscribed to happens.
  • Send notifications to the channels they have selected (email or slack)

Below is an example of different notifications they each user can subscribe to.

enter image description here

I am wondering how to do this using Laravel. My first idea was to:

  1. Create a notifications JSON column on the users table, and store it like (probably using the learnings from the Managing Mass User Settings lesson.)
{
  "todo": {
    "assigned": [
      {
        "email": true,
        "slack": true
      }
    ],
    "mentioned": [
      {
        "email": true,
        "slack": true
      }
    ]
  },
  "project": {
    "created": [
      {
        "email": true,
        "slack": true
      }
    ]
  }
}

However, I am unsure if this is good practice. Further, I am also a unsure on how to actually send out the notifications dynamically.

For sending it out, I want to use Laravels notification system:

Notification::send($user, new TodoCreated($todo));

I am not sure if this is the best way, or if it makes more sense to use an Event/Listener setup? A

Also, can I utilize the via() method on the Notification class to dynamically specify the channels according to the user settings?

Any input would be greatly appreciated.


Solution

  • I think a many-to-many relationship would be more suited to this.

    Tables:
    
    User
     - id
    
    Notifications
     - id
    
    NotificationUser <-- pivot table
     - notifcation_id
     - user_id
     - channel_id
    
    Channel
     - id
     - name 
    

    To account for these additional fields in the pivot table, define them in the User model relationship:

    class User extends Model
    {
        /**
         * The roles that belong to the user.
         */
        public function notifications()
        {
            return $this->belongsToMany(Notification::class)->withPivot(['channel_id']);
        }
    }
    

    See: https://laravel.com/docs/8.x/eloquent-relationships#retrieving-intermediate-table-columns

    This way, you can advantage of the relationship methods that Laravel (eloquent) comes with.

    i.e:

    aUser->notifications(); # Getting a list of a user's notifications
    aUser->attach(1, ['channel' => 1]); # attaching a notification to the user
    

    You can also take advantage of query scopes to retrieve one channel of notifications for the user etc

    See: https://laravel.com/docs/8.x/eloquent#query-scopes

    Then use the model/listener pattern as you had planned.