Symfony 2:多重和dynamic的数据库连接

我对SF2相当陌生,想知道如何将多个数据库的连接pipe理成一个包。 目前我有这个解决scheme – 工作正常 – 但我不知道是否是正确的方法来做到这一点….

在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或另一个,我做:

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

所以,伙计们,你认为这是一个好办法吗?

而我的第二个问题是:

如何设置dynamic数据库连接? 我的意思是我有100个数据库在我的系统,我不能所有他们在我的config.yml文件。 所以我想能够dynamic更改数据库。

谢谢您的帮助!

如果使用ConnectionFactory ,则连接到连接的事件订户将停止工作,例如stofDoctrineExtensions。

这是我的方法。 我有和ConnectionFactory空连接和EntityManager。 在工作时,我只是用Reflectionsreplace连接configuration。 适用于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) } } 

更新

更优雅的解决scheme为教条2.2 / SF 2.3(无select),为PHP5.4创build(我喜欢新的数组初始化:D)我们可以使用称为连接包装的教条function,请参阅http://docs.doctrine-project.org/项目/学说,DBAL / EN /最新/参考/ portability.html

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

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

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

接下来在你的教条configuration中注册它:

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

我们的ConnectionWrapper已经正确注册。 现在会话注入。

首先创build特殊的CompilerPass类:

 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类中logging我们的新的编译器类:

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

而这一切!

连接将根据会话属性按需创build。

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

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

优点

  1. 没有更多的思考
  2. 按需创作
  3. 优雅而强大

缺点

  1. 你必须手动清理你的实体pipe理器,或为此创build特殊的教义事件
  2. 更多的代码

您可以使用容器服务doctrine.dbal.connection_factory查看Symfony\Bundle\DoctrineBundle\ConnectionFactory

 $connectionFactory = $this->container->get('doctrine.dbal.connection_factory'); $connection = $connectionFactory->createConnection(array( 'driver' => 'pdo_mysql', 'user' => 'root', 'password' => '', 'host' => 'localhost', 'dbname' => 'foo_database', )); 

这只是一个简单的例子,但它应该让你开始。

我遇到了同样的需要为每个客户端使用相同模式的不同数据库。 由于symfony 2.3,在方法resetEntityManager弃用后,我注意到代码运行良好,没有closures连接,并没有重置(旧的实体)pipe理器。

这是我目前的工作代码:

 public function switchDatabase($dbName, $dbUser, $dbPass) { $connection = $this->container->get(sprintf('doctrine.dbal.%s_connection', 'dynamic_conn')); $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); }