I set ours up with a static database to handle login and tenancy information and a secondary database to hold user data
app/config/config.yml:
doctrine:
dbal:
default_connection: default
connections:
default:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
tenantdb:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
MyCoreBundle: ~
tenantdb:
connection: tenantdb
mappings:
MyAppBundle: ~
And then, in controllers, instead of
$something = $this->getDoctrine()
->getManager()
->getRepository('MyAppBundle:Thing')
->findAll();
we did:
$something = $this->getDoctrine()
->getManager('tenantdb')
->getRepository('MyAppBundle:Thing', 'tenantdb')
->findAll();
which you can find details of here:
http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html
Then, based on Symfony2, Dynamic DB Connection/Early override of Doctrine Service
I set up a service to switch databases based on the subdomain of the request (e.g. tenant1.example.com tenant2.example.com)
src/MyCoreBundle/Resources/config/services.yml:
services:
my.database_switcher:
class: MyCoreBundle\EventListener\DatabaseSwitcherEventListener
arguments: [@request, @doctrine.dbal.tenantdb_connection]
scope: request
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
MyCoreBundle\EventListener\DatabaseSwitcherEventListener.php
namespace MyCoreBundle\EventListener;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\DBAL\Connection;
class DatabaseSwitcherEventListener {
private $request;
private $connection;
public function __construct(Request $request, Connection $connection) {
$this->request = $request;
$this->connection = $connection;
}
public function onKernelRequest() {
$connection = $this->connection;
if (! $connection->isConnected()) {
$params = $this->connection->getParams();
$subdomain = __GET_SUBDOMAIN__();
$oldname = preg_replace (
"/_tenant_$subdomain|_template/",
'',
$params['dbname']
);
$params['dbname'] = $oldname . ($subdomain ? "_tenant_$subdomain"
: "_template");
$connection->__construct(
$params,
$connection->getDriver(),
$connection->getConfiguration(),
$connection->getEventManager()
);
$connection->connect();
}
}
}
For convenience sake, we have an "extra" tenant database called XXX_template which system admins connect to when making global changes.
The plan is that this database is copied to tenant databases on tenant create.