DEV Community

Cover image for Handling Soft Deletes and Multi-Tenant Uniqueness in Laravel
Asaba William
Asaba William

Posted on

Handling Soft Deletes and Multi-Tenant Uniqueness in Laravel

When building web applications in Laravel, two common database challenges often come up:

  1. Handling soft deletes without breaking unique constraints.
  2. Supporting multi-tenant uniqueness within a shared database.

Both can cause confusing errors if not handled carefully. In this article, I’ll break down each scenario and show how to solve them effectively.

Scenario 1: Handling Soft Deletes with Unique Constraints

The Problem

Laravel’s soft delete feature keeps deleted records in the database with a deleted_at timestamp instead of removing them permanently. This is great for data recovery and audit trails.

However, unique constraints on columns like email or username don’t ignore soft-deleted rows. So if you try to insert a new user with the same unique value as a soft-deleted user, the database will reject it — even though logically, the user is “deleted.”

How to Handle This

Option A: Restore the soft-deleted record

Before inserting, check if a record with the same unique fields exists but is soft deleted. If yes, restore it:

$user = User::withTrashed()
    ->where('email', $email)
    ->first();

if ($user && $user->trashed()) {
    $user->restore();
} else {
    User::create([
        'username' => $username,
        'email' => $email,
    ]);
}

Enter fullscreen mode Exit fullscreen mode

Option B: Use conditional unique indexes

Modify your database indexes to include deleted_at so uniqueness applies only to non-deleted records:


$table->unique(['email', 'deleted_at']);

Enter fullscreen mode Exit fullscreen mode

This is a bit more advanced and depends on your database engine.

Scenario 2: Handling Multi-Tenant Uniqueness in a Shared Database

The Problem

In multi-tenant apps where multiple businesses share the same database tables, unique fields like email must be unique within each tenant, not globally.

Without scoping uniqueness by tenant, two tenants could never have users with the same email, even though their data should be isolated.

How to Handle This

Add a tenant_id (or business_id) column to your tables and include it in unique indexes:


$table->unsignedBigInteger('tenant_id');
$table->unique(['tenant_id', 'email']);
$table->unique(['tenant_id', 'username']);

Enter fullscreen mode Exit fullscreen mode

This means:

  • Two different tenants can have users with the same email.
  • Each tenant still enforces uniqueness on their own users.

In your Laravel queries and models, always scope by tenant:


User::where('tenant_id', $tenantId)->where('email', $email)->exists();

Enter fullscreen mode Exit fullscreen mode

Summary

Scenario Problem Solution
Soft deletes + uniqueness Unique constraint fails due to soft-deleted rows Restore soft-deleted record or use conditional unique indexes
Multi-tenant uniqueness Uniqueness enforced globally across tenants Add tenant_id to unique indexes and scope queries by tenant

Want to See It in Action?

I’ve put together a full Laravel demo project demonstrating these concepts in practice, including:

  • Soft delete handling with restore logic
  • Multi-tenant uniqueness enforcement
  • Example migrations, models, and controller code

Check it out here:

https://github.com/Williamug/testing-unique-handling

Run your server and go to http://127.0.0.1:8000/users

Feel free to clone, experiment, and improve!

Conclusion

Soft deletes and multi-tenant uniqueness are often overlooked pain points in Laravel apps. Applying these simple patterns will save you hours of debugging and improve your app’s reliability.

If you found this article useful, please share or comment

Top comments (0)