5

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

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

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

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

4

4 回答 4

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/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 的“额外”租户数据库,系统管理员在进行全局更改时会连接到该数据库。计划是在租户创建时将此数据库复制到租户数据库。

于 2015-06-12T09:54:09.963 回答
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

于 2013-04-02T14:27:38.563 回答
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;
于 2013-05-09T14:49:34.397 回答
0

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

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

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

于 2013-08-09T07:14:44.907 回答