16bit / easy-multitenancy
Drop-in plugin to enable multitenancy based on SQLite dbs
Fund package maintenance!
16bit
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/16bit/easy-multitenancy
Requires
- php: ^8.2
- illuminate/console: ^11.0||^12.0
- illuminate/contracts: ^11.0||^12.0
- illuminate/database: ^11.0||^12.0
- illuminate/routing: ^11.0||^12.0
- illuminate/support: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2025-11-24 23:26:50 UTC
README
A simple, drop-in Laravel package for database-per-tenant multitenancy using SQLite. Perfect for SaaS applications where each tenant gets their own isolated SQLite database with automatic URL-based tenant identification and seamless database switching.
Installation
You can install the package via composer:
composer require 16bit/easy-multitenancy
Publish the config file:
php artisan vendor:publish --tag="easy-multitenancy-config"
This is the contents of the published config file:
return [ 'database' => [ 'path' => env('TENANT_DB_PATH', database_path('tenants')), 'connection' => env('TENANT_DB_CONNECTION', 'tenant'), 'extension' => '.sqlite', ], 'cache' => [ 'prefix_enabled' => env('TENANT_CACHE_PREFIX', true), ], 'session' => [ 'prefix_enabled' => env('TENANT_SESSION_PREFIX', true), ], 'storage' => [ 'prefix_enabled' => env('TENANT_STORAGE_PREFIX', true), 'path' => env('TENANT_STORAGE_PATH', 'tenants'), ], 'queue' => [ 'tenant_aware' => env('TENANT_QUEUE_AWARE', true), ], 'seeders' => [ // Seeders to run when creating a new tenant 'on_create' => [ // \Database\Seeders\DatabaseSeeder::class ], ], 'routes' => [ 'parameter' => 'tenant', 'middleware' => ['web'], 'auto_prefix' => env('TENANT_AUTO_PREFIX_ROUTES', true), 'excluded_routes' => [ 'home', ], 'excluded_patterns' => [ 'up', 'horizon*', 'telescope*', 'api/*', '_debugbar/*', '*.js', '*.css', '*.map', ], ], ];
Features
- Database-per-tenant architecture using SQLite
- Automatic route prefixing with tenant identification
- Seamless database switching based on URL
- Tenant-isolated storage, cache, and sessions
- Queue job tenant awareness with the
TenantAwaretrait - Artisan commands for tenant management
- Events for tenant lifecycle hooks
- Custom URL generator for tenant-aware routing
Usage
Creating a Tenant
# Interactive creation with prompts php artisan tenant:create # Create with specific name php artisan tenant:create acme # Create without user php artisan tenant:create acme --no-user
Listing Tenants
php artisan tenant:list
Running Migrations
# Migrate specific tenant php artisan tenant:migrate acme # Migrate with fresh (drop all tables) php artisan tenant:migrate acme --fresh # Migrate and seed php artisan tenant:migrate acme --seed # Migrate all tenants php artisan tenant:migrate-all
Seeding Databases
# Seed specific tenant php artisan tenant:seed acme # Seed with specific seeder class php artisan tenant:seed acme --class=DatabaseSeeder # Seed all tenants php artisan tenant:seed-all
Accessing Tenants in Code
The package automatically identifies tenants from the URL and switches the database context. All routes are automatically prefixed with {tenant} parameter.
use Bit16\EasyMultitenancy\Facades\Tenant; // Get current tenant $currentTenant = Tenant::current(); // Returns tenant identifier (e.g., 'acme') // Get current tenant ID (alias for current()) $tenantId = Tenant::id(); // Get current database path $database = Tenant::database(); // Check if tenant exists if (Tenant::exists('acme')) { // Tenant exists } // Get all tenants $tenants = Tenant::all(); // Manually switch tenant (rarely needed) Tenant::identify('acme'); // Forget current tenant context Tenant::forget();
Tenant-Aware Queue Jobs
Add the TenantAware trait to your jobs to ensure they run in the correct tenant context:
use Bit16\EasyMultitenancy\Traits\TenantAware; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class ProcessOrder implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, TenantAware; public function handle() { // Job automatically runs in the correct tenant context } }
Events
The package dispatches several events you can listen to:
use Bit16\EasyMultitenancy\Events\TenantIdentified; use Bit16\EasyMultitenancy\Events\TenantNotFound; use Bit16\EasyMultitenancy\Events\DatabaseSwitched; // Listen to tenant identified event Event::listen(TenantIdentified::class, function ($event) { // $event->tenant // $event->database }); // Listen to database switched event Event::listen(DatabaseSwitched::class, function ($event) { // $event->tenant // $event->database // $event->connection }); // Listen to tenant not found event Event::listen(TenantNotFound::class, function ($event) { // $event->tenant });
Route Configuration
By default, all routes are automatically prefixed with the tenant parameter. You can exclude specific routes:
// In config/easy-multitenancy.php 'routes' => [ 'parameter' => 'tenant', 'middleware' => ['web'], 'auto_prefix' => env('TENANT_AUTO_PREFIX_ROUTES', true), 'excluded_routes' => [ 'home', ], 'excluded_patterns' => [ 'up', 'horizon*', 'telescope*', 'api/*', '_debugbar/*', '*.js', '*.css', '*.map', ], ],
Generating URLs
The package includes a custom URL generator that automatically includes the tenant parameter:
// Generate URL to a route url('/dashboard'); // Automatically becomes /{tenant}/dashboard // Named routes route('dashboard'); // Automatically includes tenant parameter // Generate URL for a specific tenant route('dashboard', ['tenant' => 'acme']);
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Security Vulnerabilities
If you discover a security vulnerability, please email Mattia Trapani at mt@16bit.it.
Credits
License
The MIT License (MIT). Please see License File for more information.