PHPackages                             16bit/easy-multitenancy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. 16bit/easy-multitenancy

ActiveLibrary[Database &amp; ORM](/categories/database)

16bit/easy-multitenancy
=======================

Drop-in plugin to enable multitenancy based on SQLite dbs

v0.3.5(1mo ago)035[3 PRs](https://github.com/16bitsrl/easy-multitenancy/pulls)MITPHPPHP ^8.3CI passing

Since Nov 24Pushed 1mo agoCompare

[ Source](https://github.com/16bitsrl/easy-multitenancy)[ Packagist](https://packagist.org/packages/16bit/easy-multitenancy)[ Docs](https://github.com/16bitsrl/easy-multitenancy)[ GitHub Sponsors](https://github.com/16bit)[ RSS](/packages/16bit-easy-multitenancy/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (34)Versions (14)Used By (0)

Easy Multitenancy for Laravel
=============================

[](#easy-multitenancy-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/662fe8f46ab9a39aa89bd9081c02811f28d39973b578beae787d811afc0e5115/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f31366269742f656173792d6d756c746974656e616e63792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/16bit/easy-multitenancy)[![GitHub Tests Action Status](https://camo.githubusercontent.com/8ba90ac925296d1265f0bf5915723524ee1cc23181c65e769d564ca0bf543163/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f313662697473726c2f656173792d6d756c746974656e616e63792f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/16bitsrl/easy-multitenancy/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/135eb2263c78ce2625e5c67b16e95e05e331fa15487de0a39f5fc9272c0985b9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f313662697473726c2f656173792d6d756c746974656e616e63792f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/16bitsrl/easy-multitenancy/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/bf6533a4f3ea15fb7de1657acf7fffba514d18b8cc5840388eb6f3e705513d28/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f31366269742f656173792d6d756c746974656e616e63792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/16bit/easy-multitenancy)

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
------------

[](#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',
    ],

    // Optional central (landlord) connection, reachable even while a tenant is active.
    'central' => [
        'enabled' => env('TENANT_CENTRAL_ENABLED', false),
        'connection' => env('TENANT_CENTRAL_CONNECTION', 'central'),
    ],

    'cache' => [
        'prefix_enabled' => env('TENANT_CACHE_PREFIX', true),
    ],

    'session' => [
        'suffix_enabled' => env('TENANT_SESSION_SUFFIX', true),
        'use_tenant_db' => env('TENANT_SESSION_USE_DB', false),
    ],

    'storage' => [
        'suffix_enabled' => env('TENANT_STORAGE_SUFFIX', true),
        'path' => env('TENANT_STORAGE_PATH', 'tenants'),
    ],

    'track_recent_tenants' => env('TENANT_TRACK_RECENT', false),

    'recent_tenants' => [
        'cookie' => env('TENANT_RECENT_COOKIE', 'em_recent_tenants'),
        'max' => (int) env('TENANT_RECENT_MAX', 5),
        'lifetime' => (int) env('TENANT_RECENT_LIFETIME', 43200),
    ],

    'queue' => [
        'tenant_aware' => env('TENANT_QUEUE_AWARE', true),
        'strict_mode' => env('TENANT_QUEUE_STRICT', true),
        'debug_logging' => env('TENANT_QUEUE_DEBUG', false),
        'excluded_jobs' => [],
        'excluded_patterns' => [],
        'exclusion_interface' => \Bit16\EasyMultitenancy\Contracts\GlobalJob::class,
    ],

    '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
--------

[](#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**
- **Automatic tenant injection** for queued jobs
- **Artisan commands** for tenant management
- **Events** for tenant lifecycle hooks
- **Custom URL generator** for tenant-aware routing

Usage
-----

[](#usage)

### Creating a Tenant

[](#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

[](#listing-tenants)

```
php artisan tenant:list
```

### Running Migrations

[](#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

[](#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

[](#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();
```

### Events

[](#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
});
```

### Central (Landlord) Connection

[](#central-landlord-connection)

Some data is shared across all tenants (e.g. the list of tenants, global users, billing). Enable the optional central connection to keep the landlord database reachable even while a tenant connection is active:

```
// config/easy-multitenancy.php
'central' => [
    'enabled' => env('TENANT_CENTRAL_ENABLED', true),
    'connection' => env('TENANT_CENTRAL_CONNECTION', 'central'),
],
```

When enabled, a `central` connection is registered pointing at your application's default connection as configured at boot. Add the `UsesCentralConnection` trait to any model that must always query the central database, regardless of the current tenant:

```
use Bit16\EasyMultitenancy\Traits\UsesCentralConnection;
use Illuminate\Database\Eloquent\Model;

class Organization extends Model
{
    use UsesCentralConnection;
}
```

> **Storage note:** when storage isolation is enabled the package routes the **default** filesystem disk to a per-tenant directory (and registers a `tenant` disk). Calls that target the default disk are tenant-scoped; explicit `Storage::disk('local')` calls are not.

### Central Routes

[](#central-routes)

Declare landlord routes that must never be tenant-prefixed (marketing pages, tenant sign-up, the landlord dashboard) with `Tenant::centralRoutes()`:

```
use Bit16\EasyMultitenancy\Facades\Tenant;
use Illuminate\Support\Facades\Route;

Tenant::centralRoutes(function () {
    Route::get('/', [LandingController::class, 'index']);
    Route::get('/pricing', [PricingController::class, 'index']);
});
```

These routes keep their original URI (no `{tenant}/` prefix), run on the default/central connection, and are skipped by tenant identification.

### Recently Visited Tenants

[](#recently-visited-tenants)

Enable `track_recent_tenants` to keep a per-browser list of recently visited tenants in a shared cookie. Read it (typically from a central route) with `Tenant::getRecentTenants()`:

```
// config/easy-multitenancy.php
'track_recent_tenants' => env('TENANT_TRACK_RECENT', true),

// Returns ['acme' => 1717000000, 'contoso' => 1716990000] (newest first)
$recent = Tenant::getRecentTenants();
```

### Queued Jobs

[](#queued-jobs)

When `queue.tenant_aware` is enabled (default), the current tenant is automatically injected into every queued job at dispatch and restored before the job runs — no trait required. Opt a job out of tenant context by implementing the `GlobalJob` interface, listing it under `queue.excluded_jobs` / `queue.excluded_patterns`, or setting a public `$tenantAware = false` property:

```
use Bit16\EasyMultitenancy\Contracts\GlobalJob;

class BackupAllTenants implements ShouldQueue, GlobalJob
{
    // Runs in the central context, without a tenant.
}
```

### Route Configuration

[](#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

[](#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
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

If you discover a security vulnerability, please email Mattia Trapani at .

Credits
-------

[](#credits)

- [Mattia Trapani](https://github.com/zupolgec)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance93

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 84.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~23 days

Recently: every ~0 days

Total

9

Last Release

33d ago

PHP version history (2 changes)v0.1PHP ^8.2

v0.2.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/c4d043f6d69ebdba89936fb938bd273b7cbca8911f1952eb0cb5dd17d9ba8d0d?d=identicon)[zupolgec](/maintainers/zupolgec)

---

Top Contributors

[![zupolgec](https://avatars.githubusercontent.com/u/161318?v=4)](https://github.com/zupolgec "zupolgec (11 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")

---

Tags

laravel16biteasy-multitenancy

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/16bit-easy-multitenancy/health.svg)

```
[![Health](https://phpackages.com/badges/16bit-easy-multitenancy/health.svg)](https://phpackages.com/packages/16bit-easy-multitenancy)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k29.9M146](/packages/laravel-cashier)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M194](/packages/laravel-ai)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M132](/packages/laravel-pulse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)[spatie/laravel-health

Monitor the health of a Laravel application

87512.0M165](/packages/spatie-laravel-health)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
