2013-05-26 39 views
15

Dự án symfony2 của tôi có một cơ sở dữ liệu chính và nhiều cơ sở dữ liệu con. Mỗi cơ sở dữ liệu con được tạo cho mỗi người dùng, thông tin đăng nhập cơ sở dữ liệu được lưu trữ trong cơ sở dữ liệu chính. Khi người dùng đăng nhập, thông tin xác thực cơ sở dữ liệu người dùng cụ thể được lấy từ cơ sở dữ liệu chính và kết nối cơ sở dữ liệu con lý tưởng nên được thiết lập. Tôi googled cho cùng, và tôi đi accross một số giải pháp và cuối cùng đã làm như sau:Kết nối cơ sở dữ liệu động 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 

Tôi tạo ra một kết nối mặc định để kết nối với cơ sở dữ liệu chính và kết nối trống cho cơ sở dữ liệu con, tương tự như tôi đã tạo các trình quản lý thực thể. Sau đó, tôi đã tạo sự kiện mặc định nghe và thêm đoạn mã sau vào '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'); 
    .... 
} 

Đoạn mã trên thiết lập các thông số cơ sở dữ liệu con và reset bộ quản lý dynamic_em thực thể.

Khi tôi làm như sau trong một số bộ điều khiển, nó hoạt động tốt và dữ liệu nếu tìm nạp từ cơ sở dữ liệu con.

$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine 

Tuy nhiên, khi tôi sử dụng ngữ cảnh bảo mật như được thấy trong mã sau, tôi gặp lỗi 'KHÔNG ĐƯỢC CHẤP NHẬN'.

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

Làm cách nào để đặt kết nối cơ sở dữ liệu động và sử dụng ngữ cảnh bảo mật?

UPDATE: -

Sau nhiều thời gian dành cho thử và sai, và googling xung quanh, tôi nhận ra rằng security.context được thiết lập trước khi thực hiện onKernelRequest. Bây giờ câu hỏi là cách để chèn chi tiết kết nối cơ sở dữ liệu vào security.context và trong đó để tiêm?

Chúng tôi cần phải đến điểm mà bối cảnh bảo mật và DBAL được đặt và mã thông báo bảo mật được tạo và chúng tôi có thể thao tác chi tiết kết nối cơ sở dữ liệu.

Do đó, khi người trong liên kết sau đã nêu, tôi đã thực hiện thay đổi đối với mã của mình, vì đó chính xác là những gì tôi muốn thực hiện. http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413

Điều này khiến cho tôi những đoạn mã sau thêm vào dự án của tôi:

#config.yml //remains unchanged, similar to above code 

Một trình biên dịch vượt qua được tạo ra như sau:

// 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()); 
    } 
} 

Trình biên dịch thông qua như sau:

# 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]); 
     } 
    } 
} 

Mã lớp trình điều khiển như sau:

# 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()); 
    } 
} 

Mã trên đã được thêm vào dự án của tôi và tôi cho rằng đây là công việc thực tế xung quanh cho vấn đề của tôi.

Nhưng bây giờ tôi nhận được lỗi sau:

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".

thế nào, tôi có thể nhận được mã của tôi để làm việc? Tôi đặt cược rằng tôi đang làm điều gì đó sai ở đây và tôi sẽ đánh giá cao bất kỳ gợi ý và giúp đỡ.

+1

là mã bằng cách sử dụng 'security.context' chạy * trước *' cái sự kiện onKernelRequest'? một số phụ thuộc của security.context phải sử dụng 'dynamic_em' – arnaud576875

+1

Mã security.context được thêm vào cùng một hành động trong đó mã getRespository được thêm vào. Do đó tôi giả định rằng onKernelRequest được thực hiện trước security.context. –

+1

@ John Tôi vừa thử nghiệm nó với Sf 2.3.2 và giải pháp đầu tiên của bạn làm việc cho tôi. Tôi đã sử dụng một người đăng ký sự kiện để sửa đổi đối tượng kết nối. Bộ điều khiển sau này đã tìm nạp một hàng từ cơ sở dữ liệu "nô lệ" và tải người dùng hiện đang đăng nhập (đã sử dụng 'FOSUserBundle'). Bối cảnh bảo mật không được phổ biến trong giai đoạn điều phối sự kiện và ngay từ cái nhìn đầu tiên tôi nghĩ nó đơn giản là vấn đề thay đổi các ưu tiên của người nghe. Nếu bạn chưa biết nó thì có lẽ bạn có thể cho tôi biết bạn đang sử dụng phiên bản Symfony nào? – gilden

Trả lời

3

Tôi muốn đề xuất giải pháp khác cho vấn đề ban đầu của bạn.Bạn có thể sử dụng PhpFileLoader để tự động xác định thông số cho config.yml của mình.

  1. Extract bạn chính các thông số kết nối cơ sở dữ liệu vào tập tin riêng biệt:

    # 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. Tạo kịch bản PHP mới (nói DynamicParametersLoader.php) mà sẽ bơm thông số mới trong vùng chứa ứng dụng. Tôi nghĩ bạn không thể sử dụng ứng dụng symfony của mình trong tập lệnh này, nhưng bạn có thể đọc thông tin đăng nhập db chính từ biến $ container. Như sau:

    # 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. Bây giờ bạn cần phải nói cho Symfony về kịch bản của bạn và tập tin parameters.yml mới:

    # config.yml 
    imports: 
        - { resource: parameters.yml } 
        - { resource: ../../DependencyInjection/DynamicParametersLoader.php } 
    
  4. Tại bước này bạn có thể tự do sử dụng các thông số được tiêm vào bạn cấu hình:

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

Giải pháp này sẽ không hoạt động vì các tham số đã được thay thế trong trình giữ chỗ. Ngoài ra, bạn sẽ tạo lại vùng chứa mới mỗi lần bạn cần thay đổi. Nó sẽ là một vụ giết người hiệu suất. –

+0

Alexandre, có các tham số khác nhau trong trình giữ chỗ (dynamic_db_name vs main_db_name). – Troggy

+0

dynamic_db_name của tôi dựa trên url và trong DynamicParametersLoader.php tôi lấy nó từ url và sử dụng trong kết nối. –

5

ở đây, bạn cần phải thực hiện logic riêng của bạn trên của riêng bạn, i n doanh nghiệp của riêng bạn.

Hãy xem tài liệu về Doctrine về "cách tạo người quản lý tổ chức".

Sau đó tạo một dịch vụ với một API rõ ràng:

$this->get('em_factory')->getManager('name-of-my-client'); // returns an EntityManager 

Bạn không thể làm điều đó với mặc định DoctrineBundle, nó không thể sử dụng cho tính năng động.

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

Và khai báo là dịch vụ.

+0

Tôi thích câu trả lời này nhiều hơn tôi. Nó liên kết tốt hơn nhiều. – Troggy

Các vấn đề liên quan