Laravel Notifcations allow you to easily send users notifications across different channels, including email and SMS. It’s a powerful and incredibly useful feature, but depending on your application you may need to limit the amount of notifications your users receive.
We recently had this exact request from a client. Their application notifies users, across multiple teams, when a product is updated – the notifications themselves are fairly generic and are intended to just give the user a little ‘nudge’ to visit the product page to see the updates.
Under the hood, the application uses events and event listeners to determine when it needs to send a notification.
Whenever a user updates a product, we fire an
ProductUpdated event. A separate
SendProductNotifications listener is responsible for sending out the
ProductUpdated notifications when it hears the
The event carries data about the product that was updated; using this data the listener can work out which users need notifying. In this case, this is essentially all the administrators of Hubs that have access to a Product.
The issue we had was that users could make (and were making) multiple, minor updates to products in a short space of time, which caused administrators to get notified multiple times about updates to the same project. This was compounded by the fact that a user could be an administrator of multiple Hubs.
It was at this point we started to look at how we could rate limit the amount of 'product updated' notifications a user received within a specified timeframe.
A bit like Login Throttling, right?!
The first thing that came to mind when mapping out some solutions to our problem was Laravel’s Login Throttling feature. Here’s how it is described in the Laravel documentation:
“If you are using Laravel's built-in
LoginController class, the
Illuminate\Foundation\Auth\ThrottlesLogins trait will already be included in your controller. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / e-mail address and their IP address.”
This sounded like what we needed, but instead of throttling login attempts based on a user’s username, e-mail and IP address, we wanted to throttle Notifications based on the notification type, a user's ID, and the updated product's ID.
Leveraging Laravel’s RateLimiter class
As mentioned in the documentation, Laravel provides its login throttling logic through the
Illuminate\Foundation\Auth\ThrottlesLogins trait. From the trait we could determine what we would need to implement to achieve out goal:
We would need to use
Illuminate\Cache\RateLimiter to keep track of the amount of notification attempts. Specifically its
tooManyAttempts() methods, both of which require:
- a string key you want to use to identify an attempt
- the decay minutes – the number of minutes to throttle for
tooManyAttempts() also requires us to pass through the maximum amount of attempts that should be allowed, which in our case will always be 1.
Now that we knew how we’d manage the rate limiting, we needed to work out what to change in order to insert that logic into Laravel’s native 'send notification' behaviour.
As per the official Notifications documentation we were already using the Notifiable trait on our User model. The trait is composed of two additional traits:
HasDatabaseNotifications- adds the methods needed to pull notifications from the database
RoutesNotifications- adds the
notify()method thats used to send a given notification
It was clear that we would need to swap RoutesNotifications out with our own implementation that customised the
Routing Throttled Notifications
Using our own notify method was pretty simple. We created two new traits:
App\Notifications\RoutesThrottledNotifications– where our customised notify() method would live
App\Notifications\Notifiable– a copy of the
Illuminate\Notifications\Notifiabletrait, that used our new
App\Notifications\RoutesThrottledNotifications looks like this:
You’ll notice that we determine which notifications should be rate limited using the
ThrottledNotification interface. This also ensures that the Notification has both its own
Overall, this approach has allowed us to leverage code within the Laravel codebase and add a useful feature without having to modify much of the existing site code at all.