Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Multi Tenancy Bundle Laravel Package

swp/multi-tenancy-bundle

SWP MultiTenancyBundle provides flexible multi-tenant support for PHP applications, with tenant-aware services, configuration, and integration helpers for isolating data and switching tenants. Ideal for SaaS projects needing clean tenant separation.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a reality check: despite the "bundle" suffix, this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/multi-tenancy.php. If you are a Laravel developer, you will not composer require this into a Laravel app and get working tenancy — you will be reading it as a reference implementation of host-based tenant resolution, or working inside a Symfony/Superdesk Web Publisher codebase. Treat the "day-to-day leverage" as architectural first, code-level second.

Minimal steps if you are in a Symfony host app:

  1. composer require swp/multi-tenancy-bundle — note the last tagged release is June 2018, so expect Symfony 3.x/4.x-era constraints. Check composer.json require before anything else; a modern Symfony 6/7 app will need --ignore-platform-reqs gymnastics or a fork.
  2. Register the bundle (AppKernel::registerBundles() or config/bundles.php for Symfony 4 Flex).
  3. Import the bundle routing/config in app/config/config.yml — look at DependencyInjection/Configuration.php in the repo for the exact tree (tenant entity class, resolution strategy, theme paths).
  4. The bundle is a read-only subtree split of the monorepo. Issues/PRs belong in SuperdeskWebPublisher/SWP, not here. Read the parent repo's docs — the split repo has almost no README of its own.

First use case to validate end-to-end: resolve a tenant from the request host, inject that tenant into a controller/service, and render a tenant-specific template. In Symfony terms that means hitting a route with Host: acme.example.com and confirming the tenant context service returns the acme tenant and the theme/template lookup switched. If you cannot get that loop closed in an hour, stop — the bundle's assumptions (Doctrine entities, organization model, theme layout) are probably too opinionated for your app.

Implementation Patterns

Host/subdomain resolution. The core pattern is a request listener that inspects $request->getHost() (and sometimes a path prefix or header) and sets the current tenant on a context service. The Laravel analogue is a TenantMiddleware that runs before SubstituteBindings. If you are porting the idea, replicate the ordering: resolution must happen before routing decisions that depend on tenant, and before any DB query that should be tenant-scoped.

Tenant context as a scoped service. The bundle exposes a "current tenant" object that other services consume. In Laravel you'd bind this as a singleton reset per request (or use scoped() in the container). The important discipline is: never pass tenant IDs around manually — read from context. That keeps controllers, templates, and repositories uniform.

Doctrine filtering for row-level isolation. The typical mechanism is a Doctrine SQL filter or a custom repository that appends tenant_id = :current. This is the piece most worth studying: the filter must be enabled on every connection (including the one used by console commands and by Messenger consumers), and must be disabled explicitly for cross-tenant admin queries. In Laravel the equivalent is a global scope on Eloquent models, with withoutGlobalScope() escape hatches — same failure mode: the scope is silently absent in queued jobs.

Theme/template switching per tenant. The bundle's Superdesk lineage means it carries a theme-resolution concern: a tenant maps to a template directory or theme package. Pattern: resolve tenant → resolve theme name → point the Twig loader / view finder at a tenant-specific path with a shared fallback. In Laravel: view()->addNamespace() or a custom ViewFinder that prepends resources/views/tenants/{slug}.

Integration tips for a Laravel port:

  • Model tenants as a tenants table with domain, subdomain, slug, settings (JSON), and a theme column.
  • Resolve in middleware, cache the host→tenant map (Cache::rememberForever("tenant:host:$host")) and bust it on tenant save.
  • Use a single database with a tenant_id column if you are porting this bundle's approach (it assumes shared schema + filter); use schema-per-tenant only if you are deliberately diverging.
  • Keep a TenantManager that supports run($tenant, fn () => ...) for jobs, commands, and tests — this is the single most valuable abstraction and it is what makes the rest testable.
  • Seed a local wildcard domain (*.test) with valet/herd so acme.test and beta.test both resolve during development.

Testing pattern. Every feature test should be able to say $this->actingAsTenant($tenant) or wrap the assertion in Tenant::run($tenant, ...). If the bundle's context is a mutable singleton, forgetting to reset it between tests produces cross-test bleed — write a base TestCase::tearDown() that resets the context unconditionally.

Gotchas and Tips

  • AGPL-3.0 is the biggest gotcha. This is a strong copyleft license. Linking it into a network-served application obliges you to offer the corresponding source to users. For a commercial SaaS this is usually a non-starter; confirm with legal before you vendor it, and prefer reimplementing the pattern over copying the code.
  • Abandoned since 2018. No security patches, no PHP 8.x / Symfony 5+ support, zero dependents on Packagist. Treat it as documentation, not a dependency.
  • Read-only subtree split. git clone of this repo gives you a slice with no history of the surrounding bundles. Bugs must be filed against SuperdeskWebPublisher/SWP; do not expect merges here.
  • The Doctrine filter is the classic footgun. It is off by default on new connections, off in doctrine:fixtures:load, and off inside Messenger/queue workers unless you explicitly enable it. Always assert isolation with a test that queries without setting a tenant and expects zero rows.
  • Host header trust. $request->getHost() is proxy- and port-sensitive. Behind a load balancer, configure trusted proxies (Request::setTrustedProxies) or your tenant resolution will silently fall back to a default tenant. Strip ports: acme.test:8000 vs acme.test will not match naive equality checks.
  • Long-running processes leak tenant state. Under RoadRunner, Octane, or a Symfony worker, a singleton tenant context persists across requests. Reset it in a terminating listener / RequestTerminated event, not in the controller.
  • Cache keys must be tenant-prefixed. The moment two tenants share a cache store, cache:clear and tag collisions become cross-tenant data leaks. Prefix tenant:{id}: on every key, including the view cache.
  • Default-tenant fallback hides config errors. A resolver that quietly returns tenant #1 for unknown hosts will make every typo in your hosts file look like a working deployment. Log at warning when resolution falls back.
  • Extension points worth knowing: the tenant resolver (swap the strategy), the tenant context (swap storage), and the theme/template locator (swap paths). Everything else in a 2018 Symfony bundle is likely wired as a compiler pass — grep CompilerPass in DependencyInjection/ before assuming a service is overridable via config.
  • Migration path if you are on Laravel: stancl/tenancy or spatie/laravel-multitenancy cover the same ground with active maintenance; use this bundle only to sanity-check your resolution and theme-switching design, and port the ideas, not the code.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib