2013-01-23 3 views
38

Per utilizzare Gestore entità/connessione diversi in base all'URL in Symfony se abbastanza semplice. Con la seguente configurazione di routingGestore di entità multiple per FOSUserBundle

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

e dal seguente ricettario;

How to work with Multiple Entity Managers and Connections

mio regolatore sarebbe simile a questa;

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

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

e sarò in grado di recuperare informazioni sul prodotto da diversi em/connessione/database.

Ora, se aggiungo qualcosa di simile al mio routing;

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

Come posso facilmente fare il login utilizzare la connessione come definito nel variabile connessione?

Questa configurazione presuppone che ogni database disponga delle proprie informazioni di accesso utente (la tabella fos_user).

Modifica: Aggiornato informazioni di routing

Edit2:

io sono ancora nuovo con PHP/Symfony/Dottrina però, quindi ti prego di perdonarmi se ho completamente sbagliato qui. Ho provato a impostare manualmente la connessione a FOS \ UserBundle \ Doctrine \ UserManager. Quello che segue è il costruttore della 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(); 
} 

In un controller, possiamo usare il seguente metodo per cambiare il em a 'testing'

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

Per questo ho cambiato il codice di seguito per utilizzare EntityManager invece di 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(); 
} 

La mia app funziona senza errori.

Dal modo in cui funziona con il controller, ho provato a cambiare la connessione aggiungendo un parametro a questa linea, ma sta ancora utilizzando la connessione predefinita.

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

Cos'altro potrei mancare qui?

+2

Non penso che FOSUserBundle sia progettato per farlo. È possibile estendere la funzionalità, contribuendo al progetto stesso o creando un fork. O potresti scrivere il tuo UserBundle con supporto per diverse connessioni. – Sgoettschkes

+1

@Sgoettschkes: sono completamente d'accordo con te. Non sono ancora in grado di trovare una soluzione/soluzione al suo problema, ma quando lo farò, sicuramente lo condividerò qui, se non da una richiesta di pull a github :) –

+1

Hai iniettato il gestore degli oggetti corretto nel Costruttore di classe UserManager (quello di prova)? –

risposta

11

Come si può vedere, FOSUserBundle può avere solo un EntityManager. Lo si può vedere dalle impostazioni 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> 

parametro% fos_user.model_manager_name% specificato nelle impostazioni come model_manager_name

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

Così al costruttore arriva l'istanza di EntityManager, che non accetta il secondo parametro nella getRepository. Pertanto, il FOSUserBundle standard può funzionare solo con un database.


Ma questa non è la fine della storia, è Symfony :) Siamo in grado di scrivere UserManager, che può utilizzare diverse connessioni db. Nell'impostazione vedi che fos_user.user_manager è un fos_user.user_manager.default. Lo troviamo 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> 

possiamo ignorare questa classe per aggiungere un parametro aggiuntivo che determinerà il tipo di connessione che si desidera utilizzare. Inoltre da ManagerFactory è possibile ottenere l'ObjectManager desiderato. Ho scritto semplice esempio per i due per Database (se avete bisogno di più database è possibile scrivere la fabbrica per questo servizio)

definire i servizi 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% 

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

e semplice prova

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

Mi dispiace che la risposta fosse così grande. Se qualcosa non è chiaro fino alla fine, ho messo il codice su github

1

FosUserBundle non è in grado di avere più di un gestore di entità.

Il modo più semplice in cui ho trovato l'utilizzo di 2 database consiste nell'override della "checkLoginAction" di 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); 
      } 
     } 

}