2013-05-26 7 views
15

私のsymfony2プロジェクトには、メインデータベースと多くの子データベースがあります。それぞれの子データベースは各ユーザーごとに作成され、データベースの資格情報はメインデータベースに格納されます。ユーザーがログインすると、ユーザー固有のデータベース資格情報がメイン・データベースからフェッチされ、子データベース接続が理想的に確立されます。 私は同じのためにGoogleで検索し、私は解決策の数accross来て、最終的には次のことをやった:動的データベース接続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(); 

データベース接続を動的に設定してセキュリティコンテキストを使用する方法を教えてください。

UPDATE: - 試行錯誤の上に費やした多くの時間の後

、そして周りのグーグル、私はsecurity.contextonKernelRequestの実行前に設定されていることに気づきました。今質問はどのように 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: Circular reference detected for 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".

私は私のコードが動作するように取得できますか、?私はここで何か間違ったことをしていると思います。私は何かヒントや助けに感謝します。

+1

は 'security.context'を使用してコードが*' onKernelRequest'イベントの前に*走っているのですか? security.contextのいくつかの依存関係は 'dynamic_em'を使用している必要があります – arnaud576875

+1

security.contextコードは、getRespositoryコードが追加されたアクションと同じアクションに追加されます。したがって、onKernelRequestはsecurity.contextの前に実行されると仮定します。 –

+1

@John私はSf 2.3.2でそれをテストしました。最初の解決策が私のために働いています。私はイベントサブスクライバを使用して接続オブジェクトを変更しました。コントローラは後で「スレーブ」データベースから行を取り出し、現在ログインしているユーザ(「FOSUserBundle」で使用)をロードします。セキュリティコンテキストは、イベントディスパッチフェーズ中に配置され、一見すると、単にリスナーの優先順位を変更する問題であると思っていました。もしあなたがそれをまだ分かっていないのであれば、あなたはSymfonyのどのバージョンを使っているのか分かりますか? – gilden

答えて

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資格を読むことができます。次のように:

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

    # 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); 
    
  4. 今すぐあなたのスクリプトと新しいparameters.ymlファイルについてのSymfonyを伝える必要がありますここで

    # config.yml 
    ... 
         dynamic_conn: 
          dbname:   %dynamic_db_name% 
          user:    %dynamic_db_user% 
          password:   %dynamic_db_password% 
    ... 
    
+0

パラメータがプレースホルダで既に置換されているため、このソリューションは機能しません。また、変更するたびに新しいコンテナを再生成します。それはパフォーマンス殺人になるでしょう。 –

+0

Alexandre、プレースホルダには異なるパラメータがあります(dynamic_db_nameとmain_db_name)。 – Troggy

+0

私のdynamic_db_nameはURLに基​​づいており、DynamicParametersLoader.phpではURLから取得して接続します。 –

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

サービスとして宣言してください。

+0

私はこの答えが私のものより好きです。それはずっとよく整列しています。 – Troggy

関連する問題