I don’t know if I’m the only one in the world who thinks about throttling custom actions, not necessarily only logins. Laravel comes already with a
ThrottlesLogins trait prepended to the default LoginController class, and does its work wonderfully.
The problem is, if you want something like that for another controller, you need to handle the Limiter yourself, which adds a lot of verbose code to get it right.
What I find weird is the missed opportunity of the
ThrottlesLogins trait. It could be called something like
ThrottlesRequests, change some parameters, and you could have a trait that could be used anywhere in your application — even outside controllers, but it’s not the main focus — in an understandable way.
So I did that.
Throttling your Request with a pair of lines o code.
It’s always better to put a problem first so we can understand what the trait tries to solve.
In my Podcast application, I have a controller called
ShareByEmailController. As the name implies, it allows the user to share the Podcast through a given email.
As you may be suspecting, it would be very harmful to let a user spam his own podcast to whatever email list he could have in hand. We could use the
throttle middleware, but that doesn’t gives us any type of control about what to do when throttled. I mean, the middleware its good for API routes but it just slaps very unfriendly response when throttled.
That’s where my
ThrottlesRequests trait comes in. Let’s put it in action immediately:
Yeah, we only need two methods.
checkThrottling() will check if the Request has been made too many times and automatically send a
ValidationException with a
429 status code. It’s just syntactic sugar that handles everything important in one expressive line.
After your main logic executes, the
incrementAttempts() will increment the number of attempts starting from zero.
That’s pretty much it. The
ThrottlesRequests has a lot of magic borrowed from the original
ThrottlesLogins, but with some added magic to keep your Controllers lean and understandable.
There are some useful things you can change with this trait, if you check the code
- The number of maximum attempts as a simple argument.
- The response to sent. For example, return custom view or a fully rendered 429 code.
- The key to identify the action being throttled.
- If the
fireThrottledEvent()method exists, fire a custom event.
Among these, it includes other necessary methods like clearing the number of attempts, get the number of seconds to availability, or change the minutes to decay.
Of course there are some limitations (no pun intended). The trait supports only one key per controller, meaning, you can’t throttle two controller actions as these will use the same key, but in that case, you may want to elevate that “one class deeper”.
And that’s it. No need to hack your way into the Limiter provided, only a nice trait to integrate into your controllers with two lines.