Download the PHP package danplaton4/tenancy-bundle without Composer
On this page you can find all versions of the php package danplaton4/tenancy-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download danplaton4/tenancy-bundle
More information about danplaton4/tenancy-bundle
Files in danplaton4/tenancy-bundle
Package tenancy-bundle
Short Description Multi-tenant Symfony bundle - zero boilerplate, zero leaks.
License MIT
Homepage https://github.com/danplaton4/tenancy-bundle
Informations about the package tenancy-bundle
Tenancy Bundle
Multi-tenancy for Symfony. Zero boilerplate, zero leaks.
Documentation · Upgrade guide
Resolve a tenant once at the edge of the request — every Symfony subsystem reconfigures itself for the rest of the lifecycle.
- The DBAL connection switches to the tenant's database (or the Doctrine SQL filter scopes every query)
- Cache pools namespace by tenant
- The Mailer transport swaps to the tenant's SMTP/DSN
- Messenger envelopes stamp the active tenant and re-boot it on the consumer side
- Your code stays tenant-unaware:
That's it. The event-driven kernel extension does the rest.
Why this exists
Laravel has stancl/tenancy. Symfony users have been writing their own glue for years — manual $tenantId parameters, leaked queries discovered in production, half-built abstractions that don't compose with Doctrine + Messenger + Cache + Mailer at the same time. This bundle treats tenancy as a first-class kernel extension, not a database switcher bolted on top.
Quality signals
- PHPStan level 9 clean — no
@phpstan-ignore, nomixedshortcuts - 559 tests / 2,068 assertions across the unit + integration suites
- CI matrix: PHP 8.2 / 8.3 / 8.4 × Symfony 7.4 / 8.0, plus
prefer-lowest, "No Doctrine", and "No Messenger" guard builds demo-smokelive-stack gate: every push tomasterrebuilds the three-tenant FrankenPHP + Caddy + MariaDB demo and exercises tenant isolation end-to-end viabin/smoke.sh(~90s)- ASVS-L1 threat model per phase, security gate before phase verification
- Strict mode on by default — a missing tenant on a
#[TenantAware]entity is an exception, not silent data leakage
Install
Register the bundle in config/bundles.php, then run bin/console tenancy:init to scaffold config/packages/tenancy.yaml.
One-shot setup:
bin/console tenancy:installhandles both steps in a single command. It usesnikic/php-parserto AST-editconfig/bundles.phpsafely — install as a dev dependency first:composer require --dev nikic/php-parser. Without it the command exits 1 with a clear error and prints the manual snippet to paste.
Configure (config/packages/tenancy.yaml):
Mark tenant-scoped entities (shared-DB mode only):
That's the minimum. See the Documentation for resolver options, custom bootstrappers, Messenger integration, testing, and the contributor guide.
Try the demo
A runnable three-tenant Symfony app lives under examples/saas/:
Three tenants (acme, globex, initech) + a landlord page, FrankenPHP + Caddy + MariaDB 11, with the Profiler tab and Mailpit always-up. If host ports 80 / 8025 are already taken on your machine, override:
See examples/saas/README.md for the full walkthrough — three-step fallback ladder (curl Host: → /etc/hosts → browser-native *.localhost), Mailpit + Profiler walkthroughs, CI gate details.
Features
- Database-per-tenant — DBAL connection switches at runtime per tenant via
TenantDriverMiddleware, nowrapper_classconfig required - Shared-database — Doctrine SQL filter with
#[TenantAware]attribute; zero manual query scoping; strict-mode by default - 5 built-in resolvers — Host (subdomain), Origin header (SPA-friendly, priority 25, allow-listed),
X-Tenant-IDheader, query param, CLI--tenantflag. Chain in any order via config; add your own. - Cache namespace isolation — per-tenant cache pool prefixing, no cross-tenant bleed
- Mailer bootstrapper — per-tenant SMTP DSN +
From+Reply-Toheaders, sync + async safe viaX-Transportstrategy - Messenger context propagation —
TenantStampattached to every envelope, re-booted on consume; works under sync and async transports - Symfony Profiler tab — "Tenancy" panel in the WDT showing slug, label, driver, resolver, bootstrappers, error state. Auto-registered when
kernel.debug=true, compile-stripped in prod - CLI commands —
tenancy:install(one-shot setup),tenancy:init(scaffold config),tenancy:migrate(run migrations per tenant),tenancy:run(wrap any command with tenant context) - PHPUnit testing trait —
InteractsWithTenancysets up a clean tenant DB/schema per test method, real SQLite, no mocks - Custom entity support — extend
AbstractTenant(MappedSuperclass) to add columns likebrandColor,plan,billingIdwithout breaking Doctrine inheritance
How it works
The bundle hooks into the Symfony kernel via a kernel.request listener at priority 20 (above Security at 8, below Router at 32). A resolver chain identifies the tenant from the request. Once resolved, BootstrapperChain runs every registered bootstrapper to reconfigure its subsystem. On kernel.terminate, tenant context is cleared.
Bootstrappers are Symfony services tagged with tenancy.bootstrapper — add your own by implementing TenantBootstrapperInterface and tagging the service. No bundle internals to modify. See the Custom Bootstrapper guide.
Comparison
| Feature | danplaton4/tenancy-bundle | stancl/tenancy (Laravel) | RamyHakam (Symfony) | Manual |
|---|---|---|---|---|
| Database-per-tenant | ✅ | ✅ | ✅ | DIY |
| Shared-DB (SQL filter) | ✅ | ✅ | ❌ | DIY |
#[TenantAware] attribute |
✅ | ❌ (traits) | ❌ | ❌ |
| Cache isolation | ✅ | ✅ | ❌ | ❌ |
| Mailer per-tenant | ✅ | ✅ | ❌ | ❌ |
| Messenger context propagation | ✅ | ✅ | ❌ | ❌ |
| 5 resolvers incl. Origin header | ✅ | ✅ | Host only | DIY |
CLI tenant context (tenancy:run) |
✅ | ✅ | ❌ | ❌ |
| Strict mode (default ON) | ✅ | ❌ | ❌ | ❌ |
One-command setup (tenancy:install) |
✅ | N/A | ❌ | ❌ |
| PHPUnit testing trait | ✅ | ✅ | ❌ | ❌ |
| PHPStan level 9 | ✅ | ❌ | ❌ | ❌ |
| Symfony Profiler / WDT panel | ✅ | N/A | ❌ | ❌ |
| Runnable demo + CI smoke gate | ✅ | ✅ | ❌ | ❌ |
Philosophy
A data leak across tenants is a security incident, not a config mistake — so strict mode is on by default. Opt out explicitly if you understand the trade-off.
The bundle is a kernel extension, not just a database switcher: every Symfony subsystem (database, cache, queue, mailer, filesystem) participates in the tenant lifecycle through the same event-driven bootstrapper model. Doctrine is treated as an optional dependency — every entry point is guarded by class_exists / interface_exists, so the bundle installs cleanly into a Symfony app that doesn't use Doctrine at all.
Requirements
- PHP
^8.2 - Symfony
^7.4or^8.0 - Optional:
doctrine/orm ^3,doctrine/dbal ^4,doctrine/migrations,symfony/messenger,symfony/mailer
Documentation
The full docs site is published from docs/ to https://danplaton4.github.io/tenancy-bundle/.
Highlights:
- Getting started
- Database-per-tenant guide
- Shared-DB guide
- Cache isolation
- Messenger integration
- Origin-header resolver (SPA)
- Profiler tab
- Testing with
InteractsWithTenancy - Architecture (contributor guide)
Roadmap
See the roadmap on the documentation site for what's shipping next and what's tracked-but-unscheduled. Open a GitHub issue if you want something prioritized — real demand is the single strongest input to the next milestone's scope.
Contributing
See CONTRIBUTING.md. Bug reports, design discussions, and PRs are all welcome — the bundle is small enough that the first contributor read of the code can land a real change in a single session.
License
MIT License. See LICENSE.
All versions of tenancy-bundle with dependencies
nikic/php-parser Version ^5.0
symfony/cache Version ^7.4||^8.0
symfony/config Version ^7.4||^8.0
symfony/console Version ^7.4||^8.0
symfony/dependency-injection Version ^7.4||^8.0
symfony/event-dispatcher Version ^7.4||^8.0
symfony/http-foundation Version ^7.4||^8.0
symfony/http-kernel Version ^7.4||^8.0
symfony/process Version ^7.4||^8.0