我有一个Symfony2应用程序,我希望通过使用一个数据库主租户来创建多租户(有些人不认为这是多租户,但这不是真正的重点)。
documentation描述了如何实现这一目标。但是,我希望能够动态创建租户,并将新的数据库连接详细信息(和实体管理器)直接写入config.yml文件似乎很麻烦。我宁愿有一个单独的数据库来保存租户及其连接,然后根据标识符选择正确的连接/ em(例如,从应用程序的子域提取 - clientname.app.com)。
使用this approach我应该能够实现这一点,但同时可能会破坏在运行命令行命令以更新数据库模式等时指定数据库连接和/或实体管理器的能力
如果我想做的事情有意义,是否有一种聪明的方法来实现这一目标?
答案 0 :(得分:3)
我设置了一个静态数据库来处理登录和租赁信息,以及一个辅助数据库来保存用户数据
应用程序/配置/ 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: ~
然后,在控制器中,而不是
$something = $this->getDoctrine()
->getManager()
->getRepository('MyAppBundle:Thing')
->findAll();
我们做了:
$something = $this->getDoctrine()
->getManager('tenantdb')
->getRepository('MyAppBundle:Thing', 'tenantdb')
->findAll();
你可以在这里找到详细信息: http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html
然后,基于Symfony2, Dynamic DB Connection/Early override of Doctrine Service 我设置了一个服务来根据请求的子域切换数据库(例如tenant1.example.com tenant2.example.com)
的src / MyCoreBundle /资源/配置/ 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 \事件监听\ 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();
}
}
}
为方便起见,我们有一个名为XXX_template的“额外”租户数据库,系统管理员在进行全局更改时会连接到该数据库。 计划是将此数据库复制到租户创建的租户数据库。
答案 1 :(得分:2)
创建一个服务,根据用户的凭证生成自定义实体管理器。
$this->get('my.db.service')->getEmForUser('bob');
然后你的服务将是这样的
class EntityManagerService
{
function __construct($doctrine)
{ ... }
function getEmForUser($user)
{
//look up Bob's connection details in your connection db
//and get them using the globally configured entity manager
//create Entity Manager using bob's config
return $em.
}
这是最可重用的方法,它适合Symfony2使用的依赖注入模式。
您需要返回此类的实例
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityManager.php
答案 2 :(得分:0)
不知道我是否克服了您的问题范围,但我使用此连接到不同的数据库:
$connectionFactory = $this->container->get('doctrine.dbal.connection_factory');
$conn = $connectionFactory->createConnection(array(
'driver' => 'pdo_mysql',
'user' => 'mattias',
'password' => 'nkjhnjknj',
'host' => 'fs1.uuyh.se',
'dbname' => 'csmedia',
));
return $conn;
答案 3 :(得分:0)
我们的项目遇到了同样的问题。
我们在供应商包中创建了一个名为:connectionManager。
的新服务该服务是一个多项,它通过参数返回一个entityManager。