nabilhassen/laravel-usage-limiter
Track, limit, and restrict usage for users/accounts or any model in Laravel. Define per-plan limits with reset frequencies, consume/unconsume on create/delete, check remaining allowance, generate usage reports, and auto-reset via scheduled Artisan command.
Installation:
composer require nabilhassen/laravel-usage-limiter
php artisan vendor:publish --provider="NabilHassen\LaravelUsageLimiter\ServiceProvider"
php artisan migrate
Apply Trait to Model:
use NabilHassen\LaravelUsageLimiter\Traits\HasLimits;
class User extends Authenticatable
{
use HasLimits;
}
Define a Limit:
php artisan limit:create --name="projects" --allowed_amount=5 --plan="standard" --reset_frequency="every month"
Attach Limit to Model:
$user->setLimit('projects', 'standard');
Consume Limit:
$user->useLimit('projects', 'standard'); // Consume 1
$user->useLimit('projects', 'standard', 3); // Consume 3
Track API Requests for Free Users:
// In your API middleware
public function handle(Request $request, Closure $next)
{
$user = auth()->user();
if (!$user->hasEnoughLimit('api_calls', 'free')) {
abort(429, 'API limit exceeded');
}
$user->useLimit('api_calls', 'free');
return $next($request);
}
// Define limits per plan
$freeLimit = Limit::create([
'name' => 'api_calls',
'allowed_amount' => 100,
'plan' => 'free',
'reset_frequency' => 'every month'
]);
$proLimit = Limit::create([
'name' => 'api_calls',
'allowed_amount' => 1000,
'plan' => 'pro',
'reset_frequency' => 'every month'
]);
// Attach to user
$user->setLimit($freeLimit); // Free plan user
$proUser->setLimit($proLimit); // Pro plan user
// In ProjectCreated event
public function handle()
{
$user = $this->project->user;
$user->useLimit('projects', $user->plan);
}
// In ProjectDeleted event
public function handle()
{
$user = $this->project->user;
$user->unuseLimit('projects', $user->plan);
}
// In UserPlanUpdated event
public function handle()
{
$user = $this->user;
$newLimit = Limit::where('name', 'projects')->where('plan', $user->new_plan)->first();
if ($newLimit) {
$user->setLimit($newLimit);
}
}
// Bulk-create projects with limit checks
foreach ($request->projects as $projectData) {
if ($user->hasEnoughLimit('projects', $user->plan)) {
Project::create($projectData);
$user->useLimit('projects', $user->plan);
}
}
// app/Policies/ProjectPolicy.php
public function create(User $user)
{
return $user->hasEnoughLimit('projects', $user->plan);
}
// In API controller
public function store(Request $request)
{
if (!$request->user()->hasEnoughLimit('api_calls', 'free')) {
return response()->json(['error' => 'Rate limit exceeded'], 429);
}
// Process request
$request->user()->useLimit('api_calls', 'free');
return response()->json([...]);
}
// app/Observers/ProjectObserver.php
public function created(Project $project)
{
$project->user->useLimit('projects', $project->user->plan);
}
public function deleted(Project $project)
{
$project->user->unuseLimit('projects', $project->user->plan);
}
// Dispatch after project creation
ProjectCreated::dispatch($project);
// In handler
public function handle(ProjectCreated $event)
{
$event->project->user->useLimit('projects', $event->project->user->plan);
}
Cache Invalidation:
php artisan limit:cache-reset
app(\NabilHassen\LaravelUsageLimiter\LimitManager::class)->flushCache();
Reset Frequency Precision:
Negative Limit Exceptions:
unuseLimit() throws an exception if it would set usage below 0. Handle gracefully:
try {
$user->unuseLimit('projects', $user->plan, 2);
} catch (\Exception $e) {
// Log or notify admin
}
Plan Mismatches:
public function handle($request, Closure $next)
{
if ($request->user()->plan !== $request->user()->getLimitPlan('projects')) {
$request->user()->setLimit('projects', $request->user()->plan);
}
return $next($request);
}
Concurrent Requests:
DB::transaction(function () use ($user) {
$user->useLimit('api_calls', 'free');
});
Check Limit Usage:
$user->limitUsageReport(); // Full report
$user->usedLimit('projects', 'free'); // Current usage
$user->remainingLimit('projects', 'free'); // Remaining
Manual Reset:
php artisan limit:reset --model=User --limit=projects --plan=free
Or programmatically:
$user->resetLimit('projects', 'free');
Cache Inspection:
php artisan cache:table
Cache::forget('limits');
Artisan Command Debugging:
php artisan limit:list
php artisan limit:delete --name=projects --plan=free
Custom Limit Model:
Limit model:
class CustomLimit extends \NabilHassen\LaravelUsageLimiter\Models\Limit
{
protected $table = 'custom_limits';
}
'model' => \App\Models\CustomLimit::class,
Custom Relationship Name:
config/limit.php:
'relationship' => 'usage_restrictions',
$user->usage_restrictions; // Instead of $user->limits
Custom Cache Store:
config/limit.php:
'cache_store' => 'redis',
Custom Reset Logic:
resetLimit() method in your model:
public function resetLimit($limit, $plan = null)
{
// Custom logic (e.g., log reset, notify user)
parent::resetLimit($limit, $plan);
}
Event Hooks:
LimitConsumed):
// In EventServiceProvider
protected $listen = [
\NabilHassen\LaravelUsageLimiter\Events\LimitConsumed::class => [
\App\Listeners\LogLimitUsage::class,
],
];
Eager Load Limits:
$users = User::with(['limits' => function($query) {
$query->where('name', 'projects');
}])->get();
Batch Limit Checks:
$users = User::whereHas('limits', function($query) {
$query->where('name', 'projects')
->
How can I help you explore Laravel packages today?