【问题标题】:Multiple, dynamic database connections in Symfony2Symfony2 中的多个动态数据库连接
【发布时间】:2013-04-02 10:22:46
【问题描述】:

我有一个 Symfony2 应用程序,我想通过使用一个数据库 pr 租户来创建多租户(有些人不认为这是多租户,但这不是重点)。

documentation 描述了如何实现这一点。但是,我希望能够动态创建租户,并且将新的数据库连接详细信息(和实体管理器)直接写入 config.yml 文件似乎很麻烦。我宁愿有一个单独的数据库来保存租户及其连接,然后根据标识符(例如,从应用程序的子域 - clientname.app.com 获取)选择正确的连接/em。

使用this approach 我应该能够做到这一点,但同时可能会在运行用于更新数据库模式等的命令行命令时破坏指定数据库连接和/或实体管理器的能力。

如果我想做的事情有意义,有没有聪明的方法来实现这一点?

【问题讨论】:

  • 嗨,Eirik,你最终解决了吗?我遇到了同样的问题,并且正在考虑相同的方法。提前致谢!
  • @Pknife 我最终创建了一个动态创建数据库并更新 config.yml 文件的脚本。不过,我确实遇到了一些缓存问题,而且由于这是一个业余项目,它还没有投入生产。

标签: symfony saas


【解决方案1】:

我设置了一个静态数据库来处理登录和租赁信息,以及一个辅助数据库来保存用户数据

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: ~

然后,在控制器中,而不是

         $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/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();
        }
    }

}

为方便起见,我们有一个名为 XXX_template 的“额外”租户数据库,系统管理员在进行全局更改时会连接到该数据库。 计划是在租户创建时将此数据库复制到租户数据库。

【讨论】:

  • 这种动态分配数据库名称的方法存在问题,这意味着诸如php app/console doctrine:database:create --connection=tenantdb 之类的命令不再起作用,因为它们从配置文件中读取名称而不参考事件侦听器。
【解决方案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

【讨论】:

  • 感谢您的回复。然而,这并没有真正解决我担心的根源,即连接细节如何在保持命令行功能的同时保持动态。
【解决方案3】:

不知道我是否理解了你的问题,但我使用这个连接到不同的数据库:

    $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;

【讨论】:

    【解决方案4】:

    我们的项目遇到了同样的问题。

    我们在供应商捆绑包中创建了一个名为:connectionManager 的新服务。

    服务是一个multiton,它通过参数返回一个entityManager。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-15
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 2018-12-07
      • 1970-01-01
      相关资源
      最近更新 更多