2013-01-23 30 views
38

Để sử dụng Trình quản lý thực thể/Kết nối khác nhau dựa trên URL trong Symfony nếu khá dễ dàng. Với cấu hình định tuyến sauNhiều người quản lý thực thể cho FOSUserBundle

connection: 
    pattern: /a/{connection} 
    defaults: { _controller: AcmeTestBundle:User:index } 

và từ Sách nấu ăn sau;

How to work with Multiple Entity Managers and Connections

Bộ điều khiển của tôi sẽ trông giống như thế này;

class UserController extends Controller 
{ 
    public function indexAction($connection) 
    { 

     $products = $this->get('doctrine') 
      ->getRepository('AcmeStoreBundle:Product', $connection) 
      ->findAll() 
     ; 
     .................. 

và tôi sẽ có thể tìm nạp thông tin sản phẩm từ cơ sở/kết nối/cơ sở dữ liệu khác nhau.

Bây giờ, nếu tôi thêm một cái gì đó như thế này vào định tuyến của mình;

login: 
    pattern: /a/{connection}/login 
    defaults: { _controller: FOSUserBundle:Security:login } 

Làm thế nào tôi có thể dễ dàng làm cho đăng nhập sử dụng kết nối theo quy định tại kết nối biến?

Thiết lập này giả định mỗi cơ sở dữ liệu có thông tin đăng nhập người dùng riêng của họ (bảng fos_user).

Edit: Cập nhật thông tin định tuyến

Edit2:

Tôi vẫn còn mới với PHP/Symfony/Học thuyết mặc dù, vì vậy hãy tha thứ cho tôi nếu tôi hoàn toàn sai ở đây. Tôi đã cố gắng đặt kết nối theo cách thủ công tại FOS \ UserBundle \ Doctrine \ UserManager. Sau đây là các nhà xây dựng của lớp

// 
use Doctrine\Common\Persistence\ObjectManager; 
// 

public function __construct(EncoderFactoryInterface $encoderFactory, CanonicalizerInterface $usernameCanonicalizer, CanonicalizerInterface $emailCanonicalizer, ObjectManager $om, $class) 
{ 
    parent::__construct($encoderFactory, $usernameCanonicalizer, $emailCanonicalizer); 

    $this->objectManager = $om; 
    $this->repository = $om->getRepository($class); 

    $metadata = $om->getClassMetadata($class); 
    $this->class = $metadata->getName(); 
} 

Trong một bộ điều khiển, chúng ta có thể sử dụng phương pháp sau đây để thay đổi em để 'thử nghiệm'

$em = $this->get('doctrine')->getManager('testing'); 
$repository = $this->get('doctrine')->getRepository($class, 'testing') 

Cho rằng tôi đã thay đổi mã như sau để sử dụng EntityManager thay vì ObjectManager.

// 
//use Doctrine\Common\Persistence\ObjectManager; 
use Doctrine\ORM\EntityManager; 
// 

public function __construct(EncoderFactoryInterface $encoderFactory, CanonicalizerInterface $usernameCanonicalizer, CanonicalizerInterface $emailCanonicalizer, EntityManager $om, $class) 
{ 
    parent::__construct($encoderFactory, $usernameCanonicalizer, $emailCanonicalizer); 

    $this->objectManager = $om; 
    $this->repository = $om->getRepository($class); 

    $metadata = $om->getClassMetadata($class); 
    $this->class = $metadata->getName(); 
} 

Ứng dụng của tôi hoạt động tốt mà không bị lỗi.

Từ cách nó hoạt động với bộ điều khiển, tôi đã thử thay đổi kết nối bằng cách thêm tham số vào dòng này, nhưng vẫn sử dụng kết nối mặc định.

$this->repository = $om->getRepository($class, 'testing'); 

Tôi còn thiếu gì nữa ở đây?

+2

Tôi không nghĩ rằng FOSUserBundle được thiết kế để thực hiện điều đó. Bạn có thể mở rộng chức năng, hoặc bằng cách đóng góp cho bản thân dự án hoặc tạo một ngã ba. Hoặc bạn có thể viết UserBundle của riêng bạn với sự hỗ trợ cho các kết nối khác nhau. – Sgoettschkes

+1

@Sgoettschkes: Tôi hoàn toàn đồng ý với bạn. Tôi chưa thể tìm ra giải pháp/giải pháp cho vấn đề của tôi, nhưng khi tôi làm, tôi chắc chắn sẽ chia sẻ nó ở đây, nếu không phải bằng một yêu cầu kéo tại github :) –

+1

Bạn đã tiêm trình quản lý đối tượng thích hợp vào Trình tạo lớp UserManager (trình kiểm tra)? –

Trả lời

11

Như bạn thấy, FOSUserBundle chỉ có thể có một EntityManager. Bạn có thể nhìn thấy nó từ các cài đặt orm.xml

<service id="fos_user.entity_manager" factory-service="doctrine" factory-method="getManager" class="Doctrine\ORM\EntityManager" public="false"> 
    <argument>%fos_user.model_manager_name%</argument> 
</service> 

Parameter% fos_user.mô hình_manager_name% được chỉ định trong cài đặt dưới dạng model_manager_name

fos_user: 
    db_driver:   ~ # Required 
    user_class:   ~ # Required 
    firewall_name:  ~ # Required 
    model_manager_name: ~ 

Vì vậy, vào hàm tạo đến trường hợp của EntityManager, không chấp nhận tham số thứ hai trong getRepository. Do đó, FOSUserBundle chuẩn chỉ có thể làm việc với một cơ sở dữ liệu.


Nhưng đây không phải là kết thúc của câu chuyện, đó là Symfony :) Chúng ta có thể viết ra UserManager, có thể sử dụng kết nối db khác nhau. Trong thiết lập thấy rằng fos_user.user_manager là một fos_user.user_manager.default. Chúng tôi tìm thấy nó trong orm.xml

<service id="fos_user.user_manager.default" class="FOS\UserBundle\Doctrine\UserManager" public="false"> 
    <argument type="service" id="security.encoder_factory" /> 
    <argument type="service" id="fos_user.util.username_canonicalizer" /> 
    <argument type="service" id="fos_user.util.email_canonicalizer" /> 
    <argument type="service" id="fos_user.entity_manager" /> 
    <argument>%fos_user.model.user.class%</argument> 
</service> 

Chúng tôi có thể ghi đè lớp này để xác định loại kết nối bạn muốn sử dụng. Hơn nữa bởi ManagerFactory bạn có thể nhận được ObjectManager mong muốn. Tôi đã viết ví dụ đơn giản cho hai databeses (nếu bạn cần thêm cơ sở dữ liệu bạn có thể viết nhà máy của bạn cho dịch vụ này)

xác định dịch vụ của bạn trong services.yml

services: 
    acme.user_manager.conn1: 
     class: Acme\DemoBundle\Service\UserManager 
     public: true 
     arguments: 
      - @security.encoder_factory 
      - @fos_user.util.username_canonicalizer 
      - @fos_user.util.email_canonicalizer 
      - @doctrine 
      - 'conn1_manager' 
      - %fos_user.model.user.class% 

    acme.user_manager.conn2: 
     class: Acme\DemoBundle\Service\UserManager 
     public: true 
     arguments: 
      - @security.encoder_factory 
      - @fos_user.util.username_canonicalizer 
      - @fos_user.util.email_canonicalizer 
      - @doctrine 
      - 'conn2_manager' 
      - %fos_user.model.user.class% 

quản lý của bạn

/** 
* Constructor. 
* 
* @param EncoderFactoryInterface $encoderFactory 
* @param CanonicalizerInterface $usernameCanonicalizer 
* @param CanonicalizerInterface $emailCanonicalizer 
* @param RegistryInterface  $doctrine 
* @param string     $connName 
* @param string     $class 
*/ 
public function __construct(EncoderFactoryInterface $encoderFactory, CanonicalizerInterface $usernameCanonicalizer, 
          CanonicalizerInterface $emailCanonicalizer, RegistryInterface $doctrine, $connName, $class) 
{ 
    $om = $doctrine->getEntityManager($connName); 
    parent::__construct($encoderFactory, $usernameCanonicalizer, $emailCanonicalizer, $om, $class); 
} 

/** 
* Just for test 
* @return EntityManager 
*/ 
public function getOM() 
{ 
    return $this->objectManager; 
} 

và thử nghiệm đơn giản

/** 
* phpunit -c app/ src/Acme/DemoBundle/Tests/FOSUser/FOSUserMultiConnection.php 
*/ 
class FOSUserMultiConnection extends WebTestCase 
{ 
    public function test1() 
    { 
     $client = static::createClient(); 

     /** @var $user_manager_conn1 UserManager */ 
     $user_manager_conn1 = $client->getContainer()->get('acme.user_manager.conn1'); 

     /** @var $user_manager_conn2 UserManager */ 
     $user_manager_conn2 = $client->getContainer()->get('acme.user_manager.conn2'); 

     /** @var $om1 EntityManager */ 
     $om1 = $user_manager_conn1->getOM(); 
     /** @var $om2 EntityManager */ 
     $om2 = $user_manager_conn2->getOM(); 

     $this->assertNotEquals($om1->getConnection()->getDatabase(), $om2->getConnection()->getDatabase()); 
    } 
} 

Tôi rất tiếc vì câu trả lời quá lớn. Nếu có điều gì đó không rõ ràng cho đến cuối, tôi đặt mã trên github

1

FosUserBundle không thể có nhiều hơn một người quản lý thực thể.

Cách dễ nhất tôi tìm thấy để sử dụng 2 cơ sở dữ liệu, là ghi đè 'checkLoginAction' của SecurityController.

<?php 
//in myuserBunle/Controller/SecurityController.php 

class SecurityController extends BaseController 
{ 

    /** 
    * check the user information 
    */ 

    public function checkLoginAction(Request $request){ 
      $username = \trim($request->request->get("_username")); 
      $user = $this->container->get('fos_user.user_manager')->findUserByUsername($username); 
     $userDB2 = ..... 


      $password = \trim($request->request->get('_password')); 


      if ($user) { 
       // Get the encoder for the users password 
       $encoder  = $this->container->get('security.encoder_factory')->getEncoder($user); 
       $encoded_pass = $encoder->encodePassword($password, $user->getSalt()); 

       if (($user->getPassword() == $encoded_pass) || $this->checkSecondEM()) { 
       $this->logUser($request, $user); 
       return new RedirectResponse($this->container->get('router')->generate($this->container->get('session')->get('route'), $request->query->all())); 
       } else { 
       // Password bad 
        return parent::loginAction($request); 
       } 
      } else { 
       // Username bad 
       return parent::loginAction($request); 
      } 
     } 

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