Skip to content

Event system

Much of the behavior of Tenancy for Laravel is configured using events and listeners.

As explained on the How it works page, Tenancy is initialized using tenancy()->initialized(), usually called by route middleware.

That method for the most part just sets the current tenant and dispatches the TenancyInitialized event.

In response to that event, we call the BootstrapTenancy listener:

app/Providers/TenancyServiceProvider.php
Events\InitializingTenancy::class => [],
Events\TenancyInitialized::class => [
Listeners\BootstrapTenancy::class,
],

That method then calls individual bootstrappers.

The same thing happens with tenancy()->end():

app/Providers/TenancyServiceProvider.php
Events\EndingTenancy::class => [],
Events\TenancyEnded::class => [
Listeners\RevertToCentralContext::class,
],

This event-based architecture makes it easy to swap out parts of the default Tenancy logic, or more commonly, add more things to execute.

The main thing people customize is the tenant creation process. If we look at the default code, we can see that it’s a series of jobs wrapped in a job pipeline.

app/Providers/TenancyServiceProvider.php
Events\CreatingTenant::class => [],
Events\TenantCreated::class => [
JobPipeline::make([
Jobs\CreateDatabase::class,
Jobs\MigrateDatabase::class,
// Jobs\SeedDatabase::class,
// Jobs\CreateStorageSymlinks::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),
// Listeners\CreateTenantStorage::class,
],

A job pipeline is our own abstraction for turning sequences of jobs into listeners. Here, we use a job pipeline to ensure that when a tenant is created, the database is created, then the database is migrated, then optionally seeded, then perhaps a storage symlink created.

Separately from this job pipeline, there’s a CreateTenantStorage listener. It’s a listener, not a job, and it doesn’t have dependencies on the logic in jobs listed in the pipeline, therefore it can be executed separately.

A common use case for the job pipeline is to collect user information when a tenant “registers”, store that on the tenant (as virtual attributes), then use that user information to create a user in the tenant database after it has been created and fully migrated.

Importantly, since much of Tenancy depends on Laravel’s event system, using “wide” event fakes (Event::fake()) in tests is discouraged. Instead, when writing tests, try to only fake the events that you’re making assertions about, to avoid accidentally breaking Tenancy logic.