mgraichy / tenant-aware
A package for multitenancy in Laravel
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/mgraichy/tenant-aware
Requires
- laravel/prompts: ^0.3.0@dev
Requires (Dev)
- laravel/pint: dev-main
- orchestra/testbench: 9.x-dev
- pestphp/pest: 3.x-dev
- pestphp/pest-plugin-laravel: 3.x-dev
This package is auto-updated.
Last update: 2025-11-26 17:32:18 UTC
README
An extensible multitenancy package for Laravel which provides 1 database per subdomain of the application.
Installation
Install the package via Composer:
composer require mgraichy/tenant-aware
Or, clone the package from Github for testing without relying on an installed Laravel app.
Usage
This package adds a tenant_switcher table to your database. It then uses that table to switch tenants for each subdomain on the site. For example, subdomain1.example.com will be a tenant with its own database, and subdomain2.example.com will be another tenant, which also has its own database.
You will have to create the empty databases beforehand, and fill in the tenant_switcher table.
Filling In the tenant_switcher Table in Your System Database
tenant_name. The name of the company, user, or any other entity that a separate database is given to. For example,ElePHPants Inc.tenant_domain. The full domain name, including subdomain, for this tenant, e.g.elephpants.example.com.tenant_database. The name of the database created for this tenant, e.g.db_elephpants.
Configuration
- Change the
.envfile:
DB_CONNECTION=tenant
QUEUE_CONNECTION=redis
CACHE_STORE=redis
- Publish the Tenant-Aware Files:
php artisan vendor:publish --tag=tenant-aware-migrations --tag=tenant-aware-subdomains
--tag=tenant-aware-migrations. This will publish the migration for thetenant_switchertable indatabase/migrations/system-db, and add adatabase/migrations/tenantsdirectory which gives the option of migrating a specialized set of tables for tenants.'domain'. Place your root domain name (e.g.example.com) intoconfig/tenant-aware.php'sdomainkey.'additional_classes'. For how to use this key, see Extending the Package.'tenant'. The pakage dynamically takes thetenantconnection from this file and puts it into theDatabaseManagerinside Laravel's service container on every request.
--tag=tenant-aware-subdomains. This publishes the configuration file toconfig/tenant-aware.phpand the routes file toroutes/subdomains.php.
Additional Features
Artisan
The tenants:foreach Artisan command has been designed to allow any Artisan command to also be run on a per-tenant basis.
Abstractly, here's the prototype:
php artisan tenants:foreach \
<artisan-command> \
--params='<parameter-for-artisan-command>="<value>" <param2>="<value2>" ...' \
--tenant=<tenant_switcher.id|tenant_switcher.tenant_name>
Concretely, here's what you can type into the shell to install the tenant_switcher table:
php artisan tenants:foreach \
migrate \
--params='--path="database/migrations/system-db" --realpath'
Extending the Package
Create additional classes anywhere in your container and dynamically import them into this package. These classes will then be register()ed and boot()ed as though you had created an additional service provider.
Let's run through exactly how this is done:
// config/tenant-aware.php: return [ 'domain' => 'example.com', 'additional_classes' => [ [ 'FQCN' => \App\TenantAware\ClassOne::class, ], [ 'FQCN' => \App\TenantAware\ClassTwo::class, '__construct-params' => [ 'some string', \App\Models\User::class, ], '__invoke-params' => [ [4, 5, 6] ], ], ], 'tenant' => [/*...*/], ];
- Put the class(es) you've defined within the file, as above.
'FQCN'. The fully-qualified class name of each class you include in this file (if any).'__construct-params'. Any parameters used in the class(es)' constructor.'__invoke-params'. Any parameters necessary for the__invoke()method of this class.
- Motivation for the
__invoke()Magic Method. Including an__invoke()method maintains the developer's hook into the package itself, without the package needing to know the name(s) of the method(s) to call. - Motivation behind Using Arrays rather than Collections. Arrays are much more time-efficient than collections, which are wrappers around the array.
- Arrays were chosen because they are being used only as a way to move data to the service provider (and not to transform that data in any way with a collection).
Testing
If you want to test this before installation, clone this repository from Github (github.com/mgraichy/tenant-aware) and run a basic test with the CLI, switching to this package's directory and running: vendor/bin/pest.
Packages used: the Orchestral Testbench, which itself uses PHPUnit and a minimal implementation of Laravel so that you don't need a full Laravel installation, and the wonderful Pest package.
You also don't need to create actual subdomains or alter e.g., the /etc/hosts file in your dev environment because Laravel testing is ultimately based on Symfony's BrowserKit, which doesn't use networking in its requests but runs your code as though it did.
Under the Hood
This section provides a deeper dive into how the package operates under the hood. Below is a diagram illustrating the interactions within the package:
Explanation
There are 2 Connections for Every Request.
- The
TenantSwitcherModelfrom the Package's Service Provider. This determines whether the default connection will:- Switch to
"mysql"(database: your system DB), which happens when a subdomain is not provided by the enduser in the browser's URL bar, or - Remain as
"tenant", yet switch its database fromnullto the tenant's database which was gotten from thetenant_switchertable.
- Switch to
License
This package is licensed under The MIT License.
Acknowledgments and Resources
- Tom Schlick gives a clear general overview of multitenancy.
- Mohamed Said also gives a very clear explanation of how Laravel's databases work behind the scenes.
- Freek Van der Herten shared both Spatie's implementation of multitenancy (which he initially created), and other implementations as well.
- Finally, for (at least some) other definitions of multitenancy, see here and here.