我的symfony2项目有一个主数据库和许多子数据库。为每个用户创建每个子数据库,数据库凭据存储在主数据库中。当用户登录时,从主数据库中获取用户特定的数据库凭证,理想情况下应建立子数据库连接。 我用谷歌搜索了同样的东西,我遇到了许多解决方案,最后做了以下几点:
#config.yml
doctrine:
dbal:
default_connection: default
connections:
default:
dbname: maindb
user: root
password: null
host: localhost
dynamic_conn:
dbname: ~
user: ~
password: ~
host: localhost
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
auto_mapping: true
dynamic_em:
connection: dynamic_conn
auto_mapping: true
我创建了一个连接到主数据库的默认连接和一个子数据库的空连接,同样我创建了实体管理器。 然后我创建了默认事件监听器并将以下代码添加到'onKernelRequest':
public function onKernelRequest(GetResponseEvent $event) //works like preDispatch in Zend
{
//code to get db credentials from master database and stored in varaiables
....
$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_em');
....
}
上面的代码设置子数据库参数并重置dynamic_em实体管理器。
当我在某个控制器中执行以下操作时,它可以正常工作,并且如果从子数据库中获取数据。
$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine
但是,当我使用安全上下文时,如下面的代码所示,我收到错误'NO DATABASE SELECTED'。
$securityContext = $this->container->get('security.context');
$loggedinUserid = $securityContext->getToken()->getUser()->getId();
如何动态设置数据库连接并使用安全上下文?
更新: -
花了很多时间在试错和google上,我意识到security.context
是在执行onKernelRequest
之前设置的。现在问题是如何将数据库连接详细信息注入security.context, where 注入?
我们需要设置DBAL和安全上下文并创建安全令牌,我们可以操作数据库连接详细信息。
因此,正如以下链接中的人所说,我对我的代码进行了更改,因为这正是我想要做的。 http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413
这给我留下了以下代码添加到我的项目中:
#config.yml //remains unchanged, similar to above code
编译器传递创建如下:
// src/Blogger/BlogBundle/BloggerBlogBundle.php
namespace Blogger\BlogBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Blogger\BlogBundle\DependencyInjection\Compiler\CustomCompilerPass;
class BloggerBlogBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CustomCompilerPass());
}
}
编译器传递如下:
# src/Blogger/BlogBundle/DependencyInjection/Compiler/CustomCompilerPass.php
class CustomCompilerPassimplements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$connection_service = 'doctrine.dbal.dynamic_conn_connection';
if ($container->hasDefinition($connection_service))
{
$def = $container->getDefinition($connection_service);
$args = $def->getArguments();
$args[0]['driverClass'] = 'Blogger\BlogBundle\UserDependentMySqlDriver';
$args[0]['driverOptions'][] = array(new Reference('security.context'));
$def->replaceArgument(0, $args[0]);
}
}
}
驱动程序类代码如下:
# src/Blogger/BlogBundle/UserDependentMySqlDriver.php
use Doctrine\DBAL\Driver\PDOMySql\Driver;
class UserDependentMySqlDriver extends Driver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
$dbname = ..... //store database name in variable
$params['dbname'] = $dbname;
return parent::connect($params, $username, $password, array());
}
}
以上代码已添加到我的项目中,我认为这是我的问题的实际工作。
但现在我收到以下错误:
ServiceCircularReferenceException:检测到循环引用 service“security.context”,path:“profiler_listener - > profiler - > security.context - > security.authentication.manager - > fos_user.user_provider.username_email - > fos_user.user_manager - > doctrine.orm.dynamic_manager_entity_manager - > doctrine.dbal.dynamic_conn_connection”。
如何,我可以让我的代码工作?我打赌我在这里做错了什么,我很感激任何提示和帮助。
答案 0 :(得分:5)
在这里,您需要在自己的业务中自己实现自己的逻辑。
在“如何创建实体管理器”上查看Doctrine的文档。
然后使用明确的API创建服务:
$this->get('em_factory')->getManager('name-of-my-client'); // returns an EntityManager
你不能使用默认的DoctrineBundle,它不能用于动态功能。
class EmFactory
{
public function getManager($name)
{
// you can get those values:
// - autoguess, based on name
// - injection through constructor
// - other database connection
// just create constructor and inject what you need
$params = array('username' => $name, 'password' => $name, ....);
// get an EM up and running
// see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html#obtaining-the-entitymanager
return $em;
}
}
宣布为服务。
答案 1 :(得分:3)
我想为您的原始问题提出不同的解决方案。您可以使用PhpFileLoader动态定义config.yml的参数。
将主数据库连接参数提取到单独的文件中:
# src/Blogger/BlogBundle/Resources/config/parameters.yml
parameters:
main_db_name: maindb
main_db_user: root
main_db_password: null
main_db_host: localhost
创建新的PHP脚本(比如DynamicParametersLoader.php),它将在app容器中注入新参数。我认为您不能在此脚本中使用您的symfony应用程序,但您可以从$ container变量中读取主数据库凭据。如下所示:
# src/Blogger/BlogBundle/DependecyInjection/DynamicParametersLoader.php
<?php
$mainDbName = $container->getParameter('main_db_name');
$mainDbUser = $container->getParameter('main_db_user');
$mainDbPassword = $container->getParameter('main_db_password');
$mainDbHost = $container->getParameter('main_db_host');
# whatever code to query your main database for dynamic DB credentials. You cannot use your symfony2 app services here, so it ought to be plain PHP.
...
# creating new parameters in container
$container->setParameter('dynamic_db_name', $dbName);
$container->setParameter('dynamic_db_user', $dbUser);
$container->setParameter('dynamic_db_password', $dbPass);
现在您需要告诉Symfony您的脚本和新参数.yml文件:
# config.yml
imports:
- { resource: parameters.yml }
- { resource: ../../DependencyInjection/DynamicParametersLoader.php }
在此步骤中,您可以在配置中自由使用注入的参数:
# config.yml
...
dynamic_conn:
dbname: %dynamic_db_name%
user: %dynamic_db_user%
password: %dynamic_db_password%
...
答案 2 :(得分:1)
使用此处发布的事件监听器有一个非常好的解决方案:
Symfony2, Dynamic DB Connection/Early override of Doctrine Service