Back to Course |
Laravel 11 Multi-Tenancy: All You Need To Know

Event System: Delete Database when Deleting Tenant

Next, I want to demonstrate the power of event system in the stancl/tenancy package. We have seen an example earlier in this course when creating a database and migrating tables when a tenant is created. You can listen to an event and have a lot of classes executed on that event.


In the TenantServiceProvider, we have already seen a job pipeline when a tenant is being created.

app/Providers/TenantServiceProvider.php:

class TenancyServiceProvider extends ServiceProvider
{
// By default, no namespace is used to support the callable array syntax.
public static string $controllerNamespace = '';
 
public function events()
{
return [
// Tenant events
Events\CreatingTenant::class => [],
Events\TenantCreated::class => [
JobPipeline::make([
Jobs\CreateDatabase::class,
Jobs\MigrateDatabase::class,
// Jobs\SeedDatabase::class,
 
// Your own jobs to prepare the tenant.
// Provision API keys, create S3 buckets, anything you want!
 
])->send(function (Events\TenantCreated $event) {
return $event->tenant;
})->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
],
Events\SavingTenant::class => [],
Events\TenantSaved::class => [],
Events\UpdatingTenant::class => [],
Events\TenantUpdated::class => [],
Events\DeletingTenant::class => [],
Events\TenantDeleted::class => [
JobPipeline::make([
Jobs\DeleteDatabase::class,
])->send(function (Events\TenantDeleted $event) {
return $event->tenant;
})->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
],
 
// ...
];
}
 
// ...
}

Now, let's try to delete the tenant and see what happens. We have an event, TenantDeleted, and then in the job pipeline, we have the DeleteDatabase class.

app/Providers/TenantServiceProvider.php:

class TenancyServiceProvider extends ServiceProvider
{
// By default, no namespace is used to support the callable array syntax.
public static string $controllerNamespace = '';
 
public function events()
{
return [
// Tenant events
Events\CreatingTenant::class => [],
Events\TenantCreated::class => [
JobPipeline::make([
Jobs\CreateDatabase::class,
Jobs\MigrateDatabase::class,
// Jobs\SeedDatabase::class,
 
// Your own jobs to prepare the tenant.
// Provision API keys, create S3 buckets, anything you want!
 
])->send(function (Events\TenantCreated $event) {
return $event->tenant;
})->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
],
Events\SavingTenant::class => [],
Events\TenantSaved::class => [],
Events\UpdatingTenant::class => [],
Events\TenantUpdated::class => [],
Events\DeletingTenant::class => [],
Events\TenantDeleted::class => [
JobPipeline::make([
Jobs\DeleteDatabase::class,
])->send(function (Events\TenantDeleted $event) {
return $event->tenant;
})->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
],
 
// ...
];
}
 
// ...
}

Will the database be deleted? Because we use Laravel Breeze on the profile page, there is a Delete account button. Let's modify this button's behavior to delete a tenant as well.

To delete user's tenants, we must go through all its tenants and, in the transactions do the following:

  • Delete each domain for a tenant.
  • Detach the user from a tenant.
  • Delete the tenant.

app/Http/Controllers/Auth/ProfileController.php:

use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Database\Models\Domain;
 
class ProfileController extends Controller
{
// ...
 
public function destroy(Request $request): RedirectResponse
{
$request->validateWithBag('userDeletion', [
'password' => ['required', 'current_password'],
]);
 
$user = $request->user();
 
foreach ($user->tenants as $tenant) {
DB::transaction(function () use ($tenant, $user) {
Domain::where('tenant_id', $tenant->id)->delete();
$user->tenants()->detach($tenant->id);
$tenant->delete();
});
}
 
Auth::logout();
 
$user->delete();
 
$request->session()->invalidate();
$request->session()->regenerateToken();
 
return Redirect::to('/');
return Redirect::to(route('home'));
}
}

Give a name for the homepage route.

routes/web.php:

Route::get('/', function () {
return view('welcome');
})->name('home');
 
require __DIR__.'/auth.php';

I have registered with a new user with an abc subdomain.

I have two databases:

  1. tenancy is the central database.
  2. tenant_1 is the database for the tenant.

After deleting the account, the tenant's database is deleted as expected.


So, in this fashion, you can create whatever job pipeline you want. It may be with one class or have more functions or jobs. It's your personal preference.

You can find the source code for this lesson on GitHub.