Symfony 2:多个动态数据库连接 [英] Symfony 2 : multiple and dynamic database connection

查看:31
本文介绍了Symfony 2:多个动态数据库连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 SF2 很陌生,我想知道如何将多个数据库的连接管理到一个包中.目前我有这个解决方案 - 效果很好 - 但我不知道这是否是正确的方法......

I am quite new to SF2 and I was wondering how I could manage connections to severals databases into ONE bundle. For the moment I have this solution - which works fine - but I don't know if it is the right way to do it....

在 myBundle\Ressource\config\config.yml 中:

in myBundle\Ressource\config\config.yml :

doctrine:
dbal:
    default_connection:       default
    connections:
        default:
            dbname:           SERVER
            user:             root
            password:         null
            host:             localhost
        client:
            dbname:           CLIENT_134
            user:             root
            password:         null
            host:             localhost
orm:
    default_entity_manager:   default
    entity_managers:
        default:
            connection:       default
            mappings:
                MyBundle: ~
        client:
            connection:       client
            mappings:
                MyBundle: ~

然后,为了切换到 BD 或另一个,我这样做:

And then, in order to switch to one of the BD or the other, I do :

$O_ressource=  $this->get('doctrine')->getEntityManager('client');
$O_ressource=  $this->get('doctrine')->getEntityManager('default');

伙计们,你认为这是管理这个的好方法吗?

So guys, do you think it is a good way to manage this?

我的第二个问题是:

如何设置动态数据库连接?我的意思是我的系统中有 100 个数据库,我无法在 config.yml 文件中设置所有数据库.所以我希望能够即时更改数据库.

how to set up dynamic database connection? I mean I have 100 databases in my system and I can't set all them in my config.yml file. So I would like to be able to change database on the fly.

感谢您的帮助!

推荐答案

如果您使用 ConnectionFactory,附加到连接的事件订阅者将停止工作,例如 stofDoctrineExtensions.

If you use ConnectionFactory, your event subscribers attached to the connection will stop working, for example stofDoctrineExtensions.

这是我的方法.我和 ConnectionFactory 一样有空连接和 EntityManager.在工作时,我只是用反射替换了连接配置.适用于 SF 2.0.10 ;)

Here is my method. I have as with ConnectionFactory have empty connection and EntityManager. While working I just replace connection configuration by Reflections. Works on SF 2.0.10 ;)

class YourService extends ContainerAware
{ 

  public function switchDatabase($dbName, $dbUser, $dbPass) 
  {
    $connection = $this->container->get(sprintf('doctrine.dbal.%s_connection', 'dynamic_conn'));
    $connection->close();

    $refConn = new \ReflectionObject($connection);
    $refParams = $refConn->getProperty('_params');
    $refParams->setAccessible('public'); //we have to change it for a moment

    $params = $refParams->getValue($connection);
    $params['dbname'] = $dbName;
    $params['user'] = $dbUser;
    $params['password'] = $dbPass;

    $refParams->setAccessible('private');
    $refParams->setValue($connection, $params);
    $this->container->get('doctrine')->resetEntityManager('dynamic_manager'); // for sure (unless you like broken transactions)
  }
}

更新:

为 php5.4 创建的对于 dotric 2.2/sf 2.3(无重选)的更优雅的解决方案(我喜欢新的数组初始化程序:D)我们可以使用称为连接包装器的学说特性,参见 http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/portability.html

More elegant solution for doctrine 2.2 / sf 2.3 (without relection), created for php5.4 (I love new array initializer :D) We can use doctrine feature called connection wrapper, see http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/portability.html

此示例使用会话服务来临时存储连接详细信息.

This example use session service for temporary storing connection details.

首先我们必须创建特殊的连接包装器:

At first we have to create special connection wrapper:

namespace w3des\DoctrineBundle\Connection;

use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\Session\Session;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Event\ConnectionEventArgs;

/*
 * @author Dawid zulus Pakula [zulus@w3des.net]
 */
class ConnectionWrapper extends Connection
{

const SESSION_ACTIVE_DYNAMIC_CONN = 'active_dynamic_conn';

/**
 * @var Session
 */
private $session;

/**
 * @var bool
 */
private $_isConnected = false;

/**
 * @param Session $sess
 */
public function setSession(Session $sess)
{
    $this->session = $sess;
}

public function forceSwitch($dbName, $dbUser, $dbPassword)
{
    if ($this->session->has(self::SESSION_ACTIVE_DYNAMIC_CONN)) {
        $current = $this->session->get(self::SESSION_ACTIVE_DYNAMIC_CONN);
        if ($current[0] === $dbName) {
            return;
        }
    }

    $this->session->set(self::SESSION_ACTIVE_DYNAMIC_CONN, [
        $dbName,
        $dbUser,
        $dbPass
    ]);

    if ($this->isConnected()) {
        $this->close();
    }
}

/**
 * {@inheritDoc}
 */
public function connect()
{
    if (! $this->session->has(self::SESSION_ACTIVE_DYNAMIC_CONN)) {
        throw new \InvalidArgumentException('You have to inject into valid context first');
    }
    if ($this->isConnected()) {
        return true;
    }

    $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array();

    $params = $this->getParams();
    $realParams = $this->session->get(self::SESSION_ACTIVE_DYNAMIC_CONN);
    $params['dbname'] = $realParams[0];
    $params['user'] = $realParams[1];
    $params['password'] = $realParams[2];

    $this->_conn = $this->_driver->connect($params, $params['user'], $params['password'], $driverOptions);

    if ($this->_eventManager->hasListeners(Events::postConnect)) {
        $eventArgs = new ConnectionEventArgs($this);
        $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
    }

    $this->_isConnected = true;

    return true;
}

/**
 * {@inheritDoc}
 */
public function isConnected()
{
    return $this->_isConnected;
}

/**
 * {@inheritDoc}
 */
public function close()
{
    if ($this->isConnected()) {
        parent::close();
        $this->_isConnected = false;
    }
}
}

接下来在您的学说配置中注册它:

Next register it in your doctrine configuration:

…

connections:
  dynamic:
    driver:   %database_driver%
    host:     %database_host%
    port:     %database_port%
    dbname:   'empty_database'
    charset:  UTF8
    wrapper_class: 'w3des\DoctrineBundle\Connection\ConnectionWrapper'

并且我们的 ConnectionWrapper 已正确注册.现在会话注入.

And our ConnectionWrapper is properly registered. Now session injection.

首先创建特殊的CompilerPass类:

First create special CompilerPass class:

namespace w3des\DoctrineBundle\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

class ConnectionCompilerPass implements CompilerPassInterface
{

/**
 * {@inheritDoc}
 */
public function process(ContainerBuilder $container)
{
    $connection = $container
    ->getDefinition('doctrine.dbal.dynamic_connection')
    ->addMethodCall('setSession', [
        new Reference('session')
    ]);
}
}

然后我们在 *Bundle 类中记录我们的新编译器类:

And we record our new compiler class in *Bundle class:

public function build(ContainerBuilder $container)
{
    parent::build($container);
    $container->addCompilerPass(new ConnectionCompilerPass());
}

仅此而已!

将根据会话属性按需创建连接.

Connection will be created on demand, based on session properties.

要切换数据库,只需使用:

To switch database, just use:

$this->get('doctrine.dbal.dynamic_connection')->forceSwitch($dbname, $dbuser, $dbpass);

优势

  1. 不再需要反思
  2. 按需创作
  3. 优雅而强大

缺点

  1. 您必须手动清理您的实体管理器,或为此创建特殊的准则事件
  2. 更多代码

这篇关于Symfony 2:多个动态数据库连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆