Multiple entity manager for FOSUserBundle

匿名 (未验证) 提交于 2019-12-03 01:55:01

问题:

To use different Entity Manager / Connection based on URL in Symfony if fairly easy. With the following routing configuration

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

and from the following Cookbook;

How to work with Multiple Entity Managers and Connections

My controller would look something like this;

class UserController extends Controller {     public function indexAction($connection)     {          $products = $this->get('doctrine')             ->getRepository('AcmeStoreBundle:Product', $connection)             ->findAll()         ;         .................. 

and I'll be able to fetch product information from different em/connection/database.

Now, if I add something like this to my routing;

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

How can I easily make the login to use connection as defined in the connection variable?

This setup assume each database has their own user login information (the fos_user table).

Edit: Updated routing information

Edit2:

I'm still new with PHP/Symfony/Doctrine though, so please forgive me if I'm completely wrong here. I tried to manually set the connection at FOS\UserBundle\Doctrine\UserManager. The following is the constructor of the class

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

In a controller, we can use the following method to change the em to 'testing'

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

For that I changed the code to the following to use EntityManager instead of 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(); } 

My app works fine with no error.

From the way it works with the controller, I tried changing the connection by adding a parameter to this line then, but it's still using the default connection.

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

What else could I be missing here?

回答1:

As you can see, FOSUserBundle can have only one EntityManager. You can see it from the settings orm.xml

%fos_user.model_manager_name%

Parameter %fos_user.model_manager_name% specified in settings as model_manager_name

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

So into the constructor comes the instance of EntityManager, which does not accept the second parameter in the getRepository. Therefore, the standard FOSUserBundle can only work with one database.


But this is not the end of story, it's Symfony :) We can write out UserManager, that can use different db connections. In the setting see that fos_user.user_manager is a fos_user.user_manager.default. We find it in orm.xml

%fos_user.model.user.class%

We can override this class to add an additional parameter that will determine what kind of connection you want to use. Further by ManagerFactory you can get the desired ObjectManager. I wrote simple example for the two databeses (if you need more databases you can write your factory for this service)

define your services in 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% 

Your manager

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

and simple test

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

I'm sorry that the answer was so big. If something is not clear to the end, I put the code on github



回答2:

FosUserBundle is not able to have more than one entity manager.

The easiest way I found to use 2 databases, is to override the 'checkLoginAction' of the SecurityController.

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!