glimmer / tenancy
Opinionated and extended Spatie Multitenancy
Installs: 56
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 1
Open Issues: 0
pkg:composer/glimmer/tenancy
Requires
- php: ^8.3
- illuminate/console: ^11.0|^12.0
- illuminate/container: ^11.0|^12.0
- illuminate/database: ^11.0|^12.0
- illuminate/queue: ^11.0|^12.0
- illuminate/routing: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- league/flysystem-path-prefixing: ^3.28
- spatie/laravel-multitenancy: ^4.0.1
Requires (Dev)
- laravel/pint: ^1.0
- laravel/scout: ^10.11
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.7
- pestphp/pest-plugin-laravel: ^3.0
- spatie/laravel-permission: ^6.10
This package is auto-updated.
Last update: 2025-10-02 18:09:08 UTC
README
An opinionated Spatie Multitenancy extension package for Laravel
This package extends spatie/laravel-multitenancy with additional opinionated features. For example, it defaults to multi-database tenancy but takes a different approach than Spatie's implementation.
Since this package builds upon Spatie’s, it retains most original concepts and functionality. It’s recommended to review Spatie's official documentation before using this package.
What makes it opinionated?
- It uses a multi-database approach by default, creating a new connection for each tenant.
- Different landlord/tenant migrations structure.
- Auto-routing files with predefined middlewares.
- Additional tasks and tenant finders that expect the model to have certain columns.
- Allows queue jobs to run in both tenant and landlord contexts.
Installation
composer require glimmer/tenancy
All files (routes, config, migrations) can be published at once by running:
php artisan vendor:publish --provider="Glimmer\Tenancy\TenancyServiceProvider"
Features
Additional Tenant Switching Tasks:
PrefixFilesystemTask– Prefixes filesystem disk paths with the tenant key (ID) usingleague/flysystem-path-prefixing.PrefixScoutTask– Prefixes Laravel Scout index names inconfig/scout.phpwith the tenant key.PrefixSpatiePermissionTask– Adds a tenant-specific prefix to Spatie Permission cache keys.
Enhanced Tenant Switching Tasks:
SwitchDatabaseConnectionTask(Replaces Spatie’sSwitchTenantDatabaseTask) – Multi-database approach that creates a connection for each tenant using the landlord’s default connection (.envsetting) and dynamically switches between them.- A different connection can be specified per tenant by setting the
database_connectioncolumn in the tenant model. - Individual connection configurations can be overridden using the
connection_configcolumn. - The database name can be changed within the
connection_configarray by defining'database' => 'name'. - This approach improves performance by avoiding frequent reconnections but slightly increases memory usage, especially in Laravel Octane environments.
- A different connection can be specified per tenant by setting the
Tenant Finders
DomainTenantFinder– Finds the tenant by matching the request domain with the entries in the tenant model’shostsarray.SubdomainTenantFinder– Identifies the tenant using the subdomain from the request, based on thehostsarray in the model.PathTenantFinder– Determines the tenant by extracting its ID from the request path.DomainAndSubdomainTenantFinder– Matches tenants using either the request’s domain or subdomain.
The
hostsarray in the tenant model can contain a list of either domains or subdomains.
Queue Enhancements:
MakeQueueMaybeTenantAwareAction– A modifiedMakeQueueTenantAwareActionallowing jobs to run in both tenant and landlord contexts if the job implementsMaybeTenantAware.MaybeTenantAware– An interface that allows jobs to be dispatched with/without tenant context depending on the job implementation.tenant events- Allows executing certain jobs when a tenant event is fired. (Job must extendTenantEventQueue) (Jobs must be defined onmultitenancy.phpconfig file with its respective event)
Tenant events:
For these events to work, the HasTenantEvents trait must be used in the tenant model (which is already included in
Glimmer's Tenant model).
CreateDatabase– Triggered when a tenant is created.MigrateDatabase– Fired after tenant creation to apply migrations (expects migrations to be at:database/migrations/tenant).SeedDatabase– Runs database seeders upon tenant creation.- Defaults to
DatabaseSeeder.phpbut will usetenant/DatabaseSeeder.phpif it exists.
- Defaults to
Seeder Utilities:
ChainSeeding– A trait that allows chaining multiple seeders to be executed sequentially.- Can be used in any class that needs to dispatch multiple seeders in a specific order.
- Supports both seeder class names and closures as seeders.
- Example usage:
use Glimmer\Tenancy\Traits\ChainSeeding; class MySeeder extends Seeder { use ChainSeeding; public function run() { // Chain multiple seeders $this->chain([ FirstSeeder::class, SecondSeeder::class, function() { // Custom seeding logic } ]); // Dispatch the chain of seeders $this->dispatchSeeders(); } }
- When using closures, make sure to register
SerializableClosureas a maybe tenant aware job in your config:
// config/multitenancy.php 'maybe_tenant_aware_jobs' => [ // ... \Laravel\SerializableClosure\SerializableClosure::class, ],
Command Enhancements:
IsTenantAware|TenantAware– A modification toSpatie/Commands/Concerns/TenantAwaretrait enabling SQLite usage without lock issues.MaybeTenantAware– A trait for commands that can be executed with/without tenant context.tenants:artisan– A modification that uses Glimmer'sIsTenantAwaretrait to allow SQLite databases to be used without blocking commands.
Middlewares:
EnsureNoTenantSession- Prevents tenant session to be used on landlord routes.ForbidsTenant- Prevents tenant to access landlord routes.
Extended functionality:
- Tenant Model –
Glimmer/Tenancy/Models/TenantextendsSpatie\Multitenancy\Models\Tenantwith additional functionality as running events and determining the database name as expected. - Separated Route Files – Landlord (landlord.php) and tenant (tenant.php) routes are managed separately, while
web.phpremains shared. IsSharedModeltrait – Enables models to be shared between tenants and the landlord, ensuring synchronization across instances.- Automatic Route Registration – Routes are automatically registered and assigned appropriate middlewares to prevent
unauthorized access (this can be disabled in
multitenancy.phpconfig).- Tenant routes includes
NeedsTenantandEnsureValidTenantSessionmiddleware by default. - Landlord routes includes
ForbidsTenantEnsureNoTenantSessionmiddleware by default. - When using route auto-registration tenant routes names are prefixed with
tenant.and landlord routes withlandlord.. - If auto-registration is disabled, use
TenancyRoutes::landlord()and/orTenancyRoutes::tenant()to register them by hand and group the routes you need.
TenancyRoutes::landlord()->group(function () { Route::get('/dashboard', function () { return 'Landlord dashboard'; })->name('dashboard'); }); TenancyRoutes::tenant()->group(function () { Route::get('/home', function () { return 'Tenant home'; })->name('home'); });
- Tenant routes includes
Notes:
-
To avoid showing
500error onNoCurrentTenantorTenantIsForbiddenexception and instead redirect to a route, you can add the following to yourbootstrap/app.phpin to the exception handler:use Glimmer\Tenancy\Facades\LandlordTenantException; return Application::configure(basePath: dirname(__DIR__)) .... ->withExceptions(function (Exceptions $exceptions) { $exceptions->render(LandlordTenantException::redirect('your-route')); })->create();
-
As disclosed before, major structural changes are migration paths for landlord and tenant, as landlord migrations are at
database/migrationsroot and tenant migrations are atdatabase/migrations/tenant, and so for migrating the landlord you need to run:php artisan migrate
For migrating the tenant, you need to run:
php artisan tenants:artisan "migrate --path=database/migrations/tenant"Or instead, use Glimmer's tenant events, which will automatically migrate the tenant after creation.
-
The
SwitchRouteCacheTaskshould be considered hardly if needed. In many cases, middleware/controller checks can enforce route access restrictions without needing this task.