Skip to content

Instantly share code, notes, and snippets.

@scottwakefield
Created July 31, 2018 16:02
Show Gist options
  • Save scottwakefield/81da8c00e63101a20fe6ea3735c21f02 to your computer and use it in GitHub Desktop.
Save scottwakefield/81da8c00e63101a20fe6ea3735c21f02 to your computer and use it in GitHub Desktop.
<?php
namespace App\Notifications;
use Illuminate\Support\Str;
use Illuminate\Cache\RateLimiter;
use Illuminate\Support\Facades\Log;
use Illuminate\Notifications\RoutesNotifications;
trait RoutesThrottledNotifications
{
// Include the original trait, but map the notify method to another name...
use RoutesNotifications {
RoutesNotifications::notify as parentNotify;
}
// Our customised method...
public function notify($instance)
{
// Here we check whether the Notification is an instance of a new
// ThrottledNotification interface. The interface itself ensures
// that certain methods are available on the notification.
if ($instance instanceof ThrottledNotification) {
// Get the throttle key for the given Notification.
$key = $this->throttleKey($instance);
// Use the key to check whether there have been too many attempts...
if ($this->limiter()->tooManyAttempts($key, $this->maxAttempts())) {
// It's up to you what you do here. We're logging the skipped
// notifications.
return Log::notice("Skipping sending notification with key `$key`. Rate limit reached.");
}
// The attempt was OK, so we increment the limiter, passing through
// the decay minutes that the ThrottledNotification interface
// demands to be set the Notification that implements it.
$this->limiter()->hit($key, $instance->throttleDecayMinutes());
}
// Execute the original notify() method.
$this->parentNotify($instance);
}
/**
* Get the rate limiter instance.
*/
protected function limiter()
{
return app(RateLimiter::class);
}
/**
* Build the notification throttle key from the Notification class name,
* the Notification's throttle key id and the current users id.
*
* Output example: productupdated|1|10
*/
protected function throttleKey($instance)
{
return Str::lower(
class_basename($instance) . '|' . $instance->throttleKeyId() . '|' . $this->getAuthIdentifier()
);
}
/**
* Set the max attempts to 1.
*/
protected function maxAttempts()
{
return 1;
}
}
@DeBelserArne
Copy link

Hello there,

Following: https://clubstudio.co.uk/journal/rate-limiting-notifications-in-laravel I'm trying to implement this, but I seems to be msising a few things.

Do I change my notification as followed:
class UserFollowed extends ThrottledNotification implements ShouldQueue -> This was originally = class UserFollowed extends Notification implements ShouldQueue

Also, the method unreadNotifcations() seems to break:

/**
	 * Retrieve all unread notifications for a user, per 5
	 *
	 * @return user
	 */
	public function notifications()
    {
        return auth()->user()->unreadNotifications()->limit(5)->get()->toArray();
    }

https://i.gyazo.com/1741e68d43adcd0ee069bfdc0d87a615.png

Is this something I can take care of, or doesn't this work this way?

@miken32
Copy link

miken32 commented Aug 19, 2019

\Illuminate\Cache\RateLimiter::hit() takes seconds, not minutes, as the second parameter.

And your article is missing an implementation for \App\Notifications\ThrottledNotification. Easy enough to figure out, but wouldn't hurt to add it.

@scottwakefield
Copy link
Author

Hi @miken32

Thanks for the comment 👍

When the article was written, \Illuminate\Cache\RateLimiter::hit() did take minutes, but I can see that was changed to take seconds in January. I'll be sure to add a note to the article along with an example implementation of \App\Notifications\ThrottledNotification.

Thanks again for the heads up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment