Implementing User Suspension in Your Laravel Application
Tags: laravel, php, eloquent
Published: 2024-06-30 04:22:29

---
title: Implementing User Suspension in Your Laravel Application
published: true
date: 2024-06-30 03:06:28 UTC
tags: laravel,php,eloquent
canonical_url: https://raziul.dev/implementing-user-suspension-in-your-laravel-application
cover_image: https://raziul.dev/_astro/47fc0d55-a976-4145-8e1a-d671cf0a9c5a_1b48x.webp.jpg
---

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:

``` bash
php artisan make:migration add_suspension_columns_to_users_table --table=users
```

2. In the migration file, add the following code:

``` php
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'
            ]);
        });
    }
};

```

3. Run the migration

``` bash
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:

``` php
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:

```php
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
<?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:

```bash
php artisan make:notification UserSuspendedNotification
php artisan make:notification UserUnsuspendedNotification
```

2. Edit `app/Notifications/UserSuspendedNotification.php`

```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.'));
    }
}

```

3. Edit `app/Notifications/UserUnsuspendedNotification.php`

```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:

```php
$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

```bash
php artisan make:middleware CheckUserSuspension
```

2. In the middleware file `app/Http/Middleware/CheckUserSuspension.php`, add the following logic to handle restricted access for suspended users:

```php
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);
    }
}

```

3. Apply Middleware to Routes:

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

```php
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

```php
// file: 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

```

2. For Laravel 10 or older

```php
// file: 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! ❤️