Multiple entity manager for FOSUserBundle

后端 未结 2 1354
醉梦人生
醉梦人生 2020-12-05 09:55

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



        
相关标签:
2条回答
  • 2020-12-05 10:01

    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.

    <?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);   
                }
            }
    
    }
    
    0 讨论(0)
  • 2020-12-05 10:04

    As you can see, FOSUserBundle can have only one EntityManager. You can see it from the settings 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.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

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

    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

    0 讨论(0)
提交回复
热议问题