17

私の 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();

データベース接続を動的に設定し、セキュリティ コンテキストも使用するにはどうすればよいですか?

アップデート:-

試行錯誤とグーグルで多くの時間を費やした後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

コンパイラ パスは次のように作成されます。

// 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: サービス "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".

どうすればコードを動作させることができますか? 私はここで何か間違ったことをしていると思います。ヒントや助けをいただければ幸いです。

4

3 に答える 3

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
    
  2. アプリ コンテナーに新しいパラメーターを挿入する新しい PHP スクリプト (DynamicParametersLoader.php など) を作成します。このスクリプトでは symfony アプリを使用できないと思いますが、$container 変数からメインの db クレデンシャルを読み取ることができます。次のように:

    # 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);
    
  3. 次に、スクリプトと新しい parameters.yml ファイルについて Symfony に伝える必要があります。

    # config.yml
    imports:
        - { resource: parameters.yml }
        - { resource: ../../DependencyInjection/DynamicParametersLoader.php }
    
  4. このステップでは、注入されたパラメーターを設定で自由に使用できます。

    # config.yml
    ...
            dynamic_conn:
                dbname:           %dynamic_db_name%
                user:             %dynamic_db_user%
                password:         %dynamic_db_password%
    ...
    
于 2013-08-11T09:37:40.783 に答える
1

ここに投稿されたイベントリスナーを使用した非常に優れたソリューションがあります。

Symfony2、Dynamic DB Connection/Doctrine Service の早期オーバーライド

于 2014-07-05T10:12:46.670 に答える