2013-05-26 4 views
15

symfony2 프로젝트에는 기본 데이터베이스와 많은 하위 데이터베이스가 있습니다. 각 하위 데이터베이스는 각 사용자에 대해 작성되고 데이터베이스 신임은 기본 데이터베이스에 저장됩니다. 사용자가 로그인하면 사용자 별 데이터베이스 자격 증명이 기본 데이터베이스에서 가져오고 이상 적으로 하위 데이터베이스 연결이 설정되어야합니다. 나도 같은 위해 인터넷 검색, 나는 다수의 솔루션을 건너 와서 마지막으로 다음과 같은 한 : 동적 데이터베이스 연결 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 

내가 주 데이터베이스와 자식 데이터베이스에 대한 빈 연결에 연결하는 기본 연결을 생성, 유사 I을 생성 된 엔티티 관리자. 은 그 때 나는 기본 이벤트 리스너를 생성하고 '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 

그러나 다음 코드에서 볼 수있는 보안 컨텍스트를 사용할 때 '아니오 데이터베이스 선택'오류가 발생합니다.

$securityContext = $this->container->get('security.context'); 
$loggedinUserid = $securityContext->getToken()->getUser()->getId(); 

어떻게 데이터베이스 연결을 동적으로 설정하고 보안 컨텍스트를 사용할 수 있습니까?

업데이트 : -

시행 착오, 그리고 주위에 인터넷 검색에 소요되는 많은 시간 후, 나는 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

@ 존 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 파일에 대해 심포니 말할 필요 :

    다음과 같은 여기
    # 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

, 당신은 자신에 자신의 논리를 구현해야, 난 n 자신의 사업.

"엔티티 관리자를 만드는 방법"에 대한 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