动态数据库连接symfony2

17 php database symfony doctrine-orm

我的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
Run Code Online (Sandbox Code Playgroud)

我创建了一个连接到主数据库的默认连接和一个子数据库的空连接,同样我创建了实体管理器.然后我创建了默认事件监听器并将以下代码添加到'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');
    ....
}
Run Code Online (Sandbox Code Playgroud)

上面的代码设置子数据库参数并重置dynamic_em实体管理器.

当我在某个控制器中执行以下操作时,它可以正常工作,如果从子数据库中获取数据.

$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine
Run Code Online (Sandbox Code Playgroud)

但是,当我使用安全上下文时,如下面的代码所示,我收到错误'NO DATABASE SELECTED'.

$securityContext = $this->container->get('security.context');
$loggedinUserid = $securityContext->getToken()->getUser()->getId();
Run Code Online (Sandbox Code Playgroud)

如何动态设置数据库连接并使用安全上下文?

更新: -

花了很多时间在试验和错误上,并在谷歌上搜索,我意识到这security.context是在执行之前设置的onKernelRequest.现在的问题是如何注入数据库连接细节到security.context,并在那里注入?

我们需要设置DBAL和安全上下文并创建安全令牌,我们可以操作数据库连接详细信息.

因此,正如以下链接中的人所述,我对我的代码进行了更改,因为这正是我想要做的. http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413

这留给我以下代码添加到我的项目:

#config.yml //remains unchanged, similar to above code
Run Code Online (Sandbox Code Playgroud)

编译器传递创建如下:

// 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器传递如下:

# 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]);
        }
   }
}
Run Code Online (Sandbox Code Playgroud)

驱动程序类代码如下:

# 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码被添加到我的项目中,我认为这是我的问题的实际工作.

但现在我收到以下错误:

ServiceCircularReferenceException:检测到服务"security.context"的循环引用,路径:"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".

怎么样,我可以让我的代码工作?我打赌我在这里做错了什么,我会感激任何提示和帮助.

Ale*_*omé 5

在这里,您需要在自己的业务中自己实现自己的逻辑.

在"如何创建实体管理器"上查看Doctrine的文档.

然后使用明确的API创建服务:

$this->get('em_factory')->getManager('name-of-my-client'); // returns an EntityManager
Run Code Online (Sandbox Code Playgroud)

你不能使用默认的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并声明为服务.


Tro*_*ggy 3

我想针对您的原始问题提出不同的解决方案。您可以使用PhpFileLoader动态定义 config.yml 的参数。

  1. 将主要数据库连接参数提取到单独的文件中:

    # src/Blogger/BlogBundle/Resources/config/parameters.yml
    
    parameters:
        main_db_name:           maindb
        main_db_user:           root
        main_db_password:       null
        main_db_host:           localhost
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建新的 PHP 脚本(例如 DynamicParametersLoader.php),它将在应用程序容器中注入新参数。我认为您不能在此脚本中使用您的 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);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在你需要告诉 Symfony 你的脚本和新的parameters.yml 文件:

    # config.yml
    imports:
        - { resource: parameters.yml }
        - { resource: ../../DependencyInjection/DynamicParametersLoader.php }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在此步骤中,您可以在配置中自由使用注入的参数:

    # config.yml
    ...
            dynamic_conn:
                dbname:           %dynamic_db_name%
                user:             %dynamic_db_user%
                password:         %dynamic_db_password%
    ...
    
    Run Code Online (Sandbox Code Playgroud)