PHPackages                             nuelcyoung/tenantable - 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. [Framework](/categories/framework)
4. /
5. nuelcyoung/tenantable

ActiveLibrary[Framework](/categories/framework)

nuelcyoung/tenantable
=====================

Multi tenancy package for CodeIgniter 4 with multiple identification strategies, bootstrap system, and lifecycle events

00PHP

Since Feb 23Pushed 2mo agoCompare

[ Source](https://github.com/nuelcyoung/tenantable)[ Packagist](https://packagist.org/packages/nuelcyoung/tenantable)[ RSS](/packages/nuelcyoung-tenantable/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Tenantable - Multitenant Package for CodeIgniter 4
==================================================

[](#tenantable---multitenant-package-for-codeigniter-4)

A robust multitenant package for CodeIgniter 4 that provides subdomain-based tenant identification and automatic tenant isolation.

Features
--------

[](#features)

- **Subdomain-based Tenant Detection** - Automatically identifies tenants from URL subdomains
- **Multiple Isolation Strategies** - Choose what works best for your needs
- **Automatic Tenant Context** - Models automatically respect tenant boundaries
- **Superadmin Bypass** - Built-in support for platform admins
- **CLI Support** - Gracefully handles CLI requests

Requirements
------------

[](#requirements)

- PHP 8.1+
- CodeIgniter 4.0+

Architecture Options
--------------------

[](#architecture-options)

This package supports **3 isolation strategies**:

StrategyHow It WorksProsCons**tenant\_id**Shared tables with `tenant_id` columnSimple to implementRisk of leakage**Table Prefix**Separate tables per tenant (`tenant_1_students`)No leakage possibleMore complex setup**Separate DB**Different database per tenantComplete isolationMost complex---

Installation
------------

[](#installation)

```
composer require nuelcyoung/tenantable
```

---

Strategy 1: Table Prefix (Recommended)
--------------------------------------

[](#strategy-1-table-prefix-recommended)

**Best for**: Most applications. No tenant\_id leakage risks.

### How It Works

[](#how-it-works)

```
students table → tenant_1_students, tenant_2_students, ...
classes table → tenant_1_classes, tenant_2_classes, ...

```

### Setup

[](#setup)

1. **Run Migration**

```
php spark migrate -g tenantable
```

2. **Configure Filters**

```
// app/Config/Filters.php
use nuelcyoung\tenantable\Filters\TenantFilter;

class Filters extends Config\Filters
{
    public $filters = [
        'tenant' => ['before' => ['/*'], 'except' => ['health', 'api/*']],
    ];
}
```

3. **Use the Model**

```
use nuelcyoung\tenantable\Traits\TenantTablePrefixModel;

class StudentModel extends TenantTablePrefixModel
{
    protected $table = 'students';
}

// Automatic: queries tenant_1_students when tenant_id = 1
$students = $studentModel->findAll();
```

### Configuration

[](#configuration)

```
// app/Config/Tenantable.php
public $prefixFormat = 'tenant_{id}_{table}'; // Default format
public $baseDomain = 'example.com';
```

---

Strategy 2: Shared Database with tenant\_id
-------------------------------------------

[](#strategy-2-shared-database-with-tenant_id)

**Best for**: Simple applications, few tenants.

### Setup

[](#setup-1)

1. **Add tenant\_id to tables**

```
php spark make:migration add_tenant_id
```

2. **Use the Trait**

```
use nuelcyoung\tenantable\Traits\TenantableTrait;

class StudentModel extends Model
{
    use TenantableTrait;
    protected $table = 'students';
}
```

### Warning: Leakage Risks

[](#warning-leakage-risks)

Using `tenant_id` has security concerns:

- Forgetting to add trait to a model
- Raw SQL queries bypassing trait
- Joins missing tenant\_id
- IDOR attacks

**Use TenantTablePrefixTrait instead to eliminate these risks.**

---

Strategy 3: Separate Database Per Tenant
----------------------------------------

[](#strategy-3-separate-database-per-tenant)

**Best for**: Enterprise, strict compliance needs.

### Configuration

[](#configuration-1)

```
// app/Config/Tenantable.php
public $separateDatabasePerTenant = true;
```

### Tenant Database Config

[](#tenant-database-config)

```
tenants table stores:
- database_host
- database_username
- database_password
- database_name
```

### Usage

[](#usage)

```
// Automatically switches to tenant's database
$school = tenant(); // Connects to school_db
$students = $studentModel->findAll(); // Queries school_db.students
```

---

Usage Examples
--------------

[](#usage-examples)

### Helper Functions

[](#helper-functions)

```
// Get current tenant ID
$tenantId = tenant_id();

// Get tenant data
$tenant = tenant();

// Check if tenant context exists
if (has_tenant()) {
    // Safe to query
}

// Generate tenant URL
$url = tenant_url('dashboard');

// Check if admin can bypass
if (can_bypass_tenant()) {
    // Access all tenants
}
```

### Manual Tenant Setting

[](#manual-tenant-setting)

```
use nuelcyoung\tenantable\Services\TenantManager;
use nuelcyoung\tenantable\Services\TenantTableManager;

TenantManager::getInstance()->setTenantById(1);
TenantTableManager::getInstance()->setTenant(1, 'school1');
```

### Bypassing (Superadmin)

[](#bypassing-superadmin)

```
// Temporarily bypass for specific query
Model::withoutTenant(function() {
    return Model::findAll(); // All tenants
});

// Or
Model::enableTenantBypass();
// queries...
Model::disableTenantBypass();
```

---

Package Structure
-----------------

[](#package-structure)

```
src/
├── Config/
│   └── Tenantable.php
├── Database/
│   └── Migrations/
│       └── CreateTenantsTable.php
├── Exceptions/
│   ├── TenantInactiveException.php
│   └── TenantNotFoundException.php
├── Filters/
│   └── TenantFilter.php
├── Helpers/
│   └── tenantable_helper.php
├── Middleware/
│   └── TenantSecurityMiddleware.php
├── Models/
│   ├── TenantModel.php
│   └── TenantableModel.php
├── Services/
│   ├── TenantManager.php
│   ├── TenantDatabaseManager.php
│   └── TenantTableManager.php
└── Traits/
    ├── TenantTablePrefixTrait.php
    └── TenantableTrait.php

```

---

Database Schema
---------------

[](#database-schema)

The `tenants` table:

FieldTypeDescriptionidINTPrimary keysubdomainVARCHAR(50)Unique subdomainnameVARCHAR(255)Tenant namedatabase\_nameVARCHAR(100)Separate DB namedatabase\_hostVARCHAR(255)DB serverdatabase\_usernameVARCHAR(100)DB userdatabase\_passwordVARCHAR(255)DB passwordis\_activeBOOLEANTenant statussettingsJSONCustom settingscreated\_atDATETIMECreatedupdated\_atDATETIMEUpdated---

Migration for Existing Apps
---------------------------

[](#migration-for-existing-apps)

### Option A: Table Prefix (Recommended)

[](#option-a-table-prefix-recommended)

1. Create tenants in `tenants` table
2. Create new prefixed tables for each tenant:
    - `tenant_1_students` (copy of students)
    - `tenant_2_students`
3. Delete old shared tables
4. Update models to use `TenantTablePrefixModel`

### Option B: Add tenant\_id

[](#option-b-add-tenant_id)

1. Add `tenant_id` column to all tables
2. Backfill with correct tenant IDs
3. Use `TenantableTrait` in models

---

Security Features
-----------------

[](#security-features)

- **TenantContext Middleware** - Enforces tenant on all requests
- **IDOR Protection** - Validates tenant\_id in requests
- **Global Table Protection** - Mark tables as exempt from prefixing
- **Audit Logging** - Log bypass attempts

---

License
-------

[](#license)

MIT

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance56

Moderate activity, may be stable

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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.

### Community

Maintainers

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

---

Top Contributors

[![nuelcyoung](https://avatars.githubusercontent.com/u/33932707?v=4)](https://github.com/nuelcyoung "nuelcyoung (5 commits)")

### Embed Badge

![Health badge](/badges/nuelcyoung-tenantable/health.svg)

```
[![Health](https://phpackages.com/badges/nuelcyoung-tenantable/health.svg)](https://phpackages.com/packages/nuelcyoung-tenantable)
```

###  Alternatives

[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k85.0M532](/packages/laravel-passport)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

712181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)[laravel/pail

Easily delve into your Laravel application's log files directly from the command line.

91545.3M590](/packages/laravel-pail)

PHPackages © 2026

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