40 votes

Gestionnaire d'entités multiples pour FOSUserBundle

Utiliser différents Entité Gestionnaire / Connexion sur la base d'URL dans Symfony si assez facile. À la suite de la configuration du routage

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

le livre de recettes;

Comment travailler avec Plusieurs Gestionnaires d'entités et de Connexions

Mon contrôleur ressemblerait à quelque chose comme ceci;

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

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

et je vais être en mesure de récupérer des informations de produit à partir de différents em/connexion/base de données.

Maintenant, si j'ajoute quelque chose comme cela pour mon routage;

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

Comment puis-je facilement faire le login à utiliser la connexion tel que défini dans la connexion variable?

Cette configuration supposons que chaque base de données a ses propres informations de connexion utilisateur (le fos_user tableau).

Edit: mise à Jour des informations de routage

Edit2:

Je suis encore nouveau avec PHP/Symfony/Doctrine bien que, de sorte s'il vous plaît pardonnez-moi si je me trompe complètement ici. J'ai essayé de paramétrer manuellement la connexion à FOS\UserBundle\Doctrine\UserManager. Ce qui suit est le constructeur de la classe

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

Dans un contrôleur, on peut utiliser la méthode suivante pour modifier l'em de "testing"

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

Pour cela, j'ai modifié le code comme suit pour utiliser l'EntityManager au lieu de 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();
}

Mon application fonctionne très bien sans aucune erreur.

De la façon dont il fonctionne avec le contrôleur, j'ai essayé de changer la connexion par l'ajout d'un paramètre à cette ligne, mais il est encore à l'aide de la connexion par défaut.

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

Quoi d'autre pourrais-je manquer ici?

12voto

forgottenbas Points 5254

Comme vous pouvez le voir, FOSUserBundle ne peut avoir qu'un EntityManager. Vous pouvez le voir dans les paramètres 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>

Le paramètre %fos_user.model_manager_name% spécifié dans les paramètres, comme model_manager_name

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

Donc dans le constructeur vient de l'instance de l'EntityManager, qui n'accepte pas le deuxième paramètre dans la getRepository. Par conséquent, la norme FOSUserBundle ne peut travailler qu'avec une seule base de données.


Mais ce n'est pas la fin de l'histoire, c'est Symfony :) Nous pouvons écrire UserManager, qui peuvent utiliser différentes db connections. Dans le réglage de voir que fos_user.user_manager est un fos_user.user_manager.par défaut. Nous la trouvons dans 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>

Nous pouvons remplacer cette classe pour ajouter un paramètre supplémentaire qui permettra de déterminer quel type de connexion que vous souhaitez utiliser. Plus loin en ManagerFactory vous pouvez obtenir le désiré ObjectManager. J'ai écrit exemple simple pour les deux databeses (si vous avez besoin de plus de bases de données, vous pouvez écrire à votre usine pour ce service)

définissez vos services en 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%

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

et test simple

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

Je suis désolé que la réponse était si grand. Si quelque chose n'est pas clair à la fin, j'ai mis le code sur github

1voto

Salestrip Points 41

FosUserBundle ne peut pas avoir plus d'un gestionnaire d'entités.

Le moyen le plus simple que j'ai trouvé d'utiliser 2 bases de données est de remplacer le 'checkLoginAction' du 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);   
            }
        }

}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X