symfony 2/3应用程序中的多个连接和实体管理器

问题描述:

我的多租户应用程序使用主数据库,该主数据库包含有关租户(如名称等)的信息以及每个租户的应用程序特定数据库symfony 2/3应用程序中的多个连接和实体管理器

我在内部config.yml教义部构成的主some_tenant连接和实体管理。

这让我从一个控制器访问主数据库(例如,用于验证和基于子域获得租户信息some_tenantsome_tenant.my-app.com)。而且它允许我在应用程序生命周期中使用特定于租户的数据库和实体管理器。

在我的配置教义部分看起来是这样的:

doctrine: 
dbal: 
    default_connection: 'master' 
    connections: 
     master: 
      driver: pdo_mysql 
      host:  "%database_host%" 
      port:  "%database_port%" 
      dbname: "%database_name%" 
      user:  "%database_user%" 
      password: "%database_password%" 
      charset: UTF8 
     some_tenant: 
      driver: pdo_mysql 
      host:  "%database_host_some_tenant%" 
      port:  "%database_port_some_tenant%" 
      dbname: "%database_name_some_tenant%" 
      user:  "%database_user_some_tenant%" 
      password: "%database_password_some_tenant%" 
      charset: UTF8 

orm: 
    auto_generate_proxy_classes: "%kernel.debug%" 
    entity_managers: 
     master: 
      connection: master 
      mappings: 
       BEMultiTenancyBundle: ~ 
     some_tenant: 
      connection: some_tenant 
      mappings: 
       AppBundle: ~ 

来了一部分,我很不满意,并不能找到一个解决方案:

首先,租客会超过20个。它开始变得混乱,这样改变了原理配置。 然后是另一个配置文件,称为tenants.yml保持更像启用/禁用应用程式功能的信息,租户特定的主题等

该文件被加载,使用Config Component验证,和容器参数是设置,以便租户配置可在应用范围内使用。 我想将数据库证书也存储在该文件中。

  1. 我想创建基于该配置的连接和实体管理器。每个租户一个,可在应用程序生命周期中使用。

  2. 我还需要在控制台命令bin/console doctrine:schema:update --em=some_tenant上使用它们来更新每个租户的数据库schem和bin/console doctrine:schema:update --em=master以更新master数据库方案。

现在我想,要实现这一目标的唯一途径是配置参数添加到理论部分编程后的appbundle被加载,并以前学说注册表构造与给定的管理者和连接。

但我什至不能找到一个点,在那里我可以实现这一点。

是否有另一种方法来到点1和2?

尽管有一个similiar question,我不确定它是否完全是大约相同的问题,并且是3岁左右,但我想用更多的解释来发表这个问题。

+0

每个租户数据库是否具有相同的模式和实体? – Cerad

+0

是的。并且架构和映射在\ AppBundle \ Entity名称空间中定义。 –

+0

一个选择是制作一个控制台命令,它将根据tenants.yml生成您的doctrine.yml配置文件。这至少会消除一些添加新租户的手动步骤。否则,我所能建议的只是查看Doctrine Bridge中的RegisterMappingsPass,看看你是否可以做类似的事情。 – Cerad

评论中的解决方案是一种简单易行的解决方案,它可以工作。我遇到的另一个解决方案是使用一些外部条件动态替换租户数据库credentails,即请求HOST

您可以通过某种方式修改或扩展连接工厂,使其具有可用的请求堆栈(或为其他SAPI提供ENV或控制台参数的域),您可以拥有相同的实体管理器(即使default! )按需配置。

上简单的介绍一下这个看起来像

use Doctrine\Bundle\DoctrineBundle\ConnectionFactory;  
use Doctrine\Common\EventManager; 
use Doctrine\DBAL\Configuration; 
use Symfony\Component\HttpFoundation\RequestStack; 

class DynamicConnectionFactory extends Factory 
{ 
    /** @var RequestStack */ 
    private $requestStack; 

    public function __construct(array $types, RequestStack $stack) 
    { 
     parent::__construct($types); 
     $this->requestStack = $stack; 
    } 

    public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array()) 
    { 
     $host = $this->requestStack->getMasterRequest()->getHost(); 
     $params = $this->replaceParamsForHost(array $params, $host); 
     return parent::createConnection($params, $config, $eventManager, $mappingTypes); 
    } 

    private function replaceParamsForHost(array $params, $host) 
    { 
     //do your magic, i.e parse config or call Memcache service or count the stars 
     return array_replace($params, ['database' => $host]); 
    } 
} 
+0

谢谢你这个苗条的解决方案。采用这种动态方法(只使用一个配置的连接并用当前需要的连接替换当前的连接),我将不得不扩展现有的教义:schema:update命令,以便为每个租户维护不同的db方案。因此,我选择了Cerad的方式,并使用基于租户配置的控制台命令创建了原理配置,该配置完美无缺。 –

+0

如果你的模式是非常不同的,配置是唯一的方法 – ScayTrase

我认为,要在symfony 2/3管理多租户。 我们可以配置auto_mapping: false为ORM的学说。 文件:config.yml

doctrine: 
    dbal: 
     default_connection: master 
     connections: 
      master: 
       driver: pdo_mysql 
       host:  '%master_database_host%' 
       port:  '%master_database_port%' 
       dbname: '%master_database_name%' 
       user:  '%master_database_user%' 
       password: '%master_database_password%' 
       charset: UTF8 
      tenant: 
       driver: pdo_mysql 
       host:  '%tenant_database_host%' 
       port:  '%tenant_database_port%' 
       dbname: '%tenant_database_name%' 
       user:  '%tenant_database_user%' 
       password: '%tenant_database_password%' 
       charset: UTF8 

    orm: 
     default_entity_manager: master 
     auto_generate_proxy_classes: "%kernel.debug%" 
     entity_managers: 
      master: 
       connection: master 
       auto_mapping: false 
       mappings: 
        AppBundle: 
         type: yml 
         dir: Resources/master/config/doctrine 
      tenant: 
       connection: tenant 
       auto_mapping: false 
       mappings: 
        AppBundle: 
         type: yml 
         dir: Resources/tenant/config/doctrine 

之后,我们无法处理通过覆盖连接信息中request_listener喜欢的文章每个租户的连接:http://mohdhallal.github.io/blog/2014/09/12/handling-multiple-entity-managers-in-doctrine-the-smart-way/ 我希望,这一做法能帮助别人与多租户

工作

Regards, Vuong Nguyen