Skip to content

Tenancy Bundle

Multi-tenancy for Symfony. Zero boilerplate, zero leaks.

Laravel has stancl/tenancy. Symfony had nothing comparable — until now. This bundle treats tenancy as a first-class citizen of the Symfony kernel. When a tenant is resolved, every service automatically reconfigures itself: database connection switches, cache namespace isolates, Messenger stamps propagate context.


Quick Start

1. Install:

composer require danplaton4/tenancy-bundle

Register the bundle in config/bundles.php, then run bin/console tenancy:init to generate config/packages/tenancy.yaml with commented defaults.

2. Configure (config/packages/tenancy.yaml):

tenancy:
    driver: database_per_tenant
    database:
        enabled: true
// config/packages/tenancy.php
use Symfony\Config\TenancyConfig;

return static function (TenancyConfig $tenancy): void {
    $tenancy->driver('database_per_tenant');
    $tenancy->database()->enabled(true);
};

3. Mark entities (shared-DB mode):

use Doctrine\ORM\Mapping as ORM;
use Tenancy\Bundle\Attribute\TenantAware;

#[ORM\Entity]
#[TenantAware]
class Invoice
{
    // Doctrine SQL filter automatically scopes queries to the active tenant
}

That's it. Subdomain requests resolve tenants, database connections switch, cache isolates.

Installation guide Getting started


Features

Feature Description
Database-per-tenant DBAL connection switching at runtime via Doctrine\DBAL\Driver\Middleware
Shared-database Doctrine SQL filter with #[TenantAware] attribute
4 built-in resolvers Subdomain, X-Tenant-ID header, query param, CLI --tenant
Cache isolation Per-tenant cache namespace — no cross-tenant bleed
Messenger context TenantStamp on every envelope, re-booted on consume
CLI commands tenancy:init, tenancy:migrate, and tenancy:run
PHPUnit trait InteractsWithTenancy for clean per-test tenant setup
Strict mode TenantMissingException when querying without tenant (default: ON)

How It Works

Request --> Router (priority 32)
              |
       TenantContextOrchestrator (priority 20)
              |
        ResolverChain
        (Host / Header / QueryParam / Console)
              |
       TenantResolved event
              |
       BootstrapperChain.boot()
        - DatabaseSwitchBootstrapper
        - DoctrineBootstrapper
        - CacheBootstrapper
              |
       TenantBootstrapped event
              |
         Controller runs
              |
       kernel.terminate
              |
       TenantContextCleared event

Bootstrappers are Symfony services tagged tenancy.bootstrapper. Add your own by implementing TenantBootstrapperInterface.

Architecture deep dive


Comparison

Feature tenancy-bundle stancl/tenancy (Laravel) RamyHakam (Symfony) Manual
Database-per-tenant DIY
Shared-DB (SQL filter) DIY
#[TenantAware] attribute
Cache isolation
Messenger context
Subdomain + domain resolution DIY
CLI tenant context
Strict mode (default ON)
tenancy:init scaffolding N/A
PHPUnit testing trait
PHPStan level 9

Requirements

  • PHP ^8.2
  • Symfony ^7.4 or ^8.0
  • Optional: doctrine/orm, doctrine/dbal, doctrine/migrations, symfony/messenger