Installation
composer require boruta/multi-tenancy-bundle
Register the bundle in config/bundles.php:
return [
// ...
Boruta\MultiTenancyBundle\BorutaMultiTenancyBundle::class => ['all' => true],
];
Configure Tenant Provider
Define a tenant provider (e.g., TenantProviderInterface) in config/packages/boruta_multi_tenancy.yaml:
boruta_multi_tenancy:
tenant_provider: App\Service\TenantProvider
default_tenant: 'main'
First Use Case: Tenant-Aware Entity Manager
Inject TenantEntityManager into a service:
use Boruta\MultiTenancyBundle\Doctrine\TenantEntityManager;
class MyService {
public function __construct(private TenantEntityManager $em) {}
}
Tenant Resolution
TenantProviderInterface to resolve tenants (e.g., from subdomains, headers, or auth):
class TenantProvider implements TenantProviderInterface {
public function getTenant(): ?string {
return $_SERVER['HTTP_HOST'] === 'tenant1.example.com' ? 'tenant1' : null;
}
}
Database Switching
SwitchDbEvent to customize connection logic:
use Boruta\MultiTenancyBundle\Event\SwitchDbEvent;
$eventDispatcher->addListener(SwitchDbEvent::class, function (SwitchDbEvent $event) {
$event->setConnectionParams(['host' => 'tenant-db-' . $event->getTenant()]);
});
Schema Isolation
TenantAwareSchemaManager for tenant-specific schema operations:
$schemaManager = $this->em->getConnection()->getSchemaManager();
$schemaManager->createTable(new Table('tenant_specific_table'));
Migrations
php bin/console doctrine:migrations:execute --tenant=tenant1
AbstractGuardAuthenticator to resolve tenants pre-auth.TenantAwareLoader for tenant-specific fixtures.TenantAwareDataProvider for tenant-aware collections.Connection Pooling
TenantEntityManager is scoped per request (use request scope in DI).Migration Conflicts
tenant: prefix for tenant-specific tables.Caching
$this->em->getConnection()->getConfiguration()->clearMetadataCache();
SwitchDbEvent to verify tenant resolution:
boruta_multi_tenancy:
debug: true
tenant_connection_failed event listeners for fallback logic.Custom Tenant Providers
TenantProviderInterface for dynamic tenant resolution (e.g., JWT claims).Event Subscribers
TenantAwareSubscriber to inject tenant logic into Doctrine lifecycle events.Tenant-Aware Services
TenantAwareInterface for automatic tenant context:
class TenantAwareService implements TenantAwareInterface {
public function setTenant(string $tenant): void { /* ... */ }
}
default_tenant in config matches a valid database connection.%env(TENANT_DB_PREFIX)% for dynamic host/port in config/packages/doctrine.yaml.How can I help you explore Laravel packages today?