Multitenancy
Intro
Multitenancy allows the app to be delivered by different domain names (tenants) and, in addition, allows for scoped content, data & functionality across the different tenants.
Implementation
The base multitenancy functionality is provided by the Spatie multitenancy package laravel-multitenancy
| This package can make a Laravel app tenant aware. The philosophy of this package is that it should only provide the bare essentials to enable multitenancy.
The package is largely un-opinionated and allows you to implement multitenancy as you wish. The documentation is really good, and outlines the basic usage.
Set up
In order to allow for multitenancy the following .env variables need to be set prior to DB migration.
APP_NAME="Envoy"
APP_URL=https://envoy.test
During migration the landlord tenant will be seeded using the LandlordTenantSeeder::class. This will set the tenant branding using default images in the public asset folder. It will also set the default theme for the tenant based on a pre-set theme.
Landlord tenant
The landlord tenant has the ID of 1 which is set using the config variable config('multitenancy.landlord_id'). The landlord branding can be adjusted in the admin panel just like the other tenants post seeding.
Tenant switching tasks
The laravel-multitenancy package provides for the addition of tasks to run when switching tenants. These are added in the 'switch_tenant_tasks' attribute in multitenancy.php config file.
This application uses the following tasks:
- SwitchTenantThemeTask::class
Tenant themes
Tenants can be given a particular theme. These themes are preset in the app.scss file and new themes can be added ad any time. Once a new theme is added it also needs to be added to the theme select of the TenantResource.
Features
Each tenant can have features scoped to it via the manage admin panel. These features are managed in the Features.php enum file. To add a feature you can add it to the enum file and it will then be seeded in the ProductionSeeder class via the FeatureSeeder.
In order to determine if a feature is set on a tenant you can use the model method $tenant->hasFeature(Features::CATALOGUE_MODULES->value).
Objective Approval Feature
The objective approval feature is a tenant scoped feature. This means that it can be turned on or off for each tenant. The feature can be issued/retracted from any tenant in App Management Panel. With this feature turned on, once objective reaches the status of 'Complete', approval of the Objective can then take place which will be able to be undertaken by persons with the appropriate permissions (see Objective Docs).
If the feature is turned off, the Objective will be able to be marked as complete and this shall be the final status of the Objective.
Importantly Objectives are not tenant scoped. This means that if the feature is turned on for a tenant, all existing objectives will be in an unapproved state.
Settings
Settings are scoped to the tenant using the ScopedToTenantTrait.
Tenant Clusters
Tenants can be organized into logical groups called Tenant Clusters. This feature is useful for categorizing and filtering tenants, especially in environments with many tenants.
Key features of Tenant Clusters:
- Each tenant can belong to one cluster (or none)
- A cluster can have multiple tenants
- Tenants can be filtered by cluster using the
ofTenantCluster()scope
For more detailed information, see the Tenant Clusters documentation.
Scoped to tenant trait
A custom trait ScopedToTenantTrait has been built to make it possible to scope the model to the current tenant on queries. This trait needs to tbe added to any scoped models using the column tenant_id
This trait does 3 things:
- Adds a creating boot method to set the
tenant_idon creation of the model - Adds a global scope to the model to only include landlord and scoped records
- Adds a query scope
ofCurrentTenant()to allow eloquent query scoping to just the current tenant.
The query scope can be used wherever the data needs to be scoped. For example, for the lessons index table. On a Neo Campus, only the lessons built in that campus are editable. So the lessons query needs to be: Lesson::ofCurrentTenant()