Skip to content
Implementing User Suspension in Your Laravel Application

Implementing User Suspension in Your Laravel Application

Published on
by Raziul Islam

This guide will walk through implementing user suspension in a Laravel application. This functionality allows you to temporarily or permanently suspend users and notify them accordingly.

Step 1: Add Suspension Columns to the Users Table

First, we need to update our users table to include columns for tracking suspension status and reason.

  1. Create a new migration:
Terminal window
php artisan make:migration add_suspension_columns_to_users_table --table=users
  1. In the migration file, add the following code:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->timestamp('suspended_at')->nullable();
$table->timestamp('suspension_ends_at')->nullable();
$table->string('suspension_reason')->nullable();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'suspended_at',
'suspension_ends_at',
'suspension_reason'
]);
});
}
};
  1. Run the migration
Terminal window
php artisan migrate

Step 2: Update the User Model

  1. Open app/Models/User.php file.
  2. Use the Suspendable trait and add the necessary attribute casts:
use App\Traits\Suspendable;
...
class User extends Authenticatable
{
...
use Suspendable;
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'suspended_at' => 'datetime',
'suspension_ends_at' => 'datetime',
];
...
}

Laravel 11 and newer versions utilize casts methods for property casting:

protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'suspended_at' => 'datetime',
'suspension_ends_at' => 'datetime',
];
}

Step 3: Create the Suspendable Trait

  1. Create a new PHP file at app/Traits/Suspendable.php
  2. Add the following code to that file:
<?php
namespace App\Traits;
use App\Notifications\UserSuspendedNotification;
use App\Notifications\UserUnsuspendedNotification;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Casts\Attribute;
trait Suspendable
{
/**
* Account is banned for lifetime.
*/
protected function isBanned(): Attribute
{
return Attribute::get(
fn () => $this->suspended_at && is_null($this->suspension_ends_at)
);
}
/**
* Account is suspended for some time.
*/
protected function isSuspended(): Attribute
{
return Attribute::get(
fn () => $this->suspended_at && $this->suspension_ends_at?->isFuture()
);
}
/**
* Suspend account and notify them.
*/
public function suspend(string $reason, CarbonInterface $ends_at = null): void
{
$this->update([
'suspended_at' => now(),
'suspension_reason' => $reason,
'suspension_ends_at' => $ends_at,
]);
$this->notify(new UserSuspendedNotification($this));
}
/**
* Un-suspend account and notify them.
*/
public function unsuspend(): void
{
if (! $this->suspended_at) {
return;
}
$this->update([
'suspended_at' => null,
'suspension_reason' => null,
'suspension_ends_at' => null,
]);
$this->notify(new UserUnsuspendedNotification($this));
}
}

This trait adds the suspend and unsuspend methods to the User model for suspending and unsuspending accounts easily. This also provides the is_banned and is_suspended attributes for checking suspension status.

Step 4: Create Notifications

  1. Create notification classes:
Terminal window
php artisan make:notification UserSuspendedNotification
php artisan make:notification UserUnsuspendedNotification
  1. Edit app/Notifications/UserSuspendedNotification.php
namespace App\Notifications;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class UserSuspendedNotification extends Notification
{
use Queueable;
public function __construct(
public readonly User $user
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(object $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
if ($this->user->is_banned) {
$subject = __('Your account has been banned');
$message = null;
} else {
$subject = __('Your account has been suspended');
$message = __('Suspension will end on: :date', ['date' => $this->user->suspention_ends_at])
}
return (new MailMessage)
->subject($subject)
->line($subject)
->line(__('Reason: **:reason**', ['reason' => $this->user->suspension_reason]))
->line($message)
->line(__('If you believe this is a mistake, please contact us.'))
->line(__('Thank you for your understanding.'));
}
}
  1. Edit app/Notifications/UserUnsuspendedNotification.php
namespace App\Notifications;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class UserUnsuspendedNotification extends Notification
{
use Queueable;
public function __construct(
public readonly User $user
) {
}
/**
* Get the notification's delivery channels.
*/
public function via(object $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Your Suspension Removed'))
->greeting(__('Hello :name,', ['name' => $this->user->name]))
->line(__('Suspension has been removed. Your account is now active.'))
->line(__('You can now log into your account.'))
->action(__('Log in'), route('login'))
->line(__('Thank you for staying with us.'));
}
}

We are almost done 😀 Let’s take a look at the usage example:

$user = \App\Models\User::find(1);
// temporary suspension (for 7 days)
$user->suspend('suspension reason', now()->addDays(7));
// permanent suspension
$user->suspend('suspension reason');
// unsuspension
$user->unsuspend();

Now, The only thing that remains is to check whether the authenticated user is suspended and restrict their access to the application. Let’s do this in the next step.

Step 5: Restrict Application Access for Suspended Users

  1. Create Middleware
Terminal window
php artisan make:middleware CheckUserSuspension
  1. In the middleware file app/Http/Middleware/CheckUserSuspension.php, add the following logic to handle restricted access for suspended users:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckUserSuspension
{
public function handle(Request $request, Closure $next): Response
{
$user = $request->user();
abort_if(
$user && ($user->is_suspended || $user->is_banned),
Response::HTTP_FORBIDDEN,
__('Your account has been suspended or banned. Check your email for details.')
);
return $next($request);
}
}
  1. Apply Middleware to Routes:

In routes/web.php or routes/api.php apply the middleware to the routes you want to protect:

use App\Http\Middleware\CheckUserSuspension;
// Protected routes
Route::group(['middleware' => ['auth', CheckUserSuspension::class]], function () {
Route::get('/dashboard', DashboardController::class]);
});
// Other routes

Otherwise, you can add this middleware to the web or api middleware group to apply it to a set of routes.

Step 6: Applying to Middleware Groups (optional)

  1. Laravel 11 or newer
bootstrap/app.php
use App\Http\Middleware\CheckUserSuspension;
return Application::configure(basePath: dirname(__DIR__))
// other code
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
CheckUserSuspension::class,
]);
})
// other code
  1. For Laravel 10 or older
app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// other middlewares
CheckUserSuspension::class,
],
'api' => [
// other middlewares
CheckUserSuspension::class,
],
];

Conclusion

By following this guide, you have successfully implemented user suspension functionality in your Laravel application. This approach keeps your User model clean and encapsulates the suspension logic within a reusable Suspendable trait.

This feature allows you to manage user access effectively by suspending and unsuspending users as needed. This not only enhances the security and control over user activities but also ensures a better user management system.

Happy coding! ❤️

Raziul Islam

Raziul Islam

Creating future-proof web solutions with cutting-edge technologies like Laravel, Livewire, and JavaScript.

Posts you might like