2013-01-23 12 views
38

Um andere Entity Manager/Verbindung basierend auf URL in Symfony zu verwenden, wenn ziemlich einfach. Mit der folgenden Routing-KonfigurationMultiple Entity Manager für FOSUserBundle

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

und aus dem folgenden Kochbuch;

How to work with Multiple Entity Managers and Connections

Mein Controller würde wie folgt aussehen;

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

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

und ich werde Produktinformationen aus verschiedenen em/Verbindung/Datenbank abrufen können.

Jetzt, wenn ich etwas zu meinem Routing hinzufügen;

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

Wie kann ich leicht die Login machen Verbindung zu verwenden, wie in der Verbindung Variable definiert?

Bei dieser Konfiguration wird davon ausgegangen, dass jede Datenbank über eigene Benutzeranmeldeinformationen verfügt (die Tabelle fos_user).

Edit: Aktualisiert Routing-Informationen

Edit2:

Ich bin neu noch mit PHP/Symfony/Lehre aber, so wenden Sie sich bitte verzeiht mir, wenn ich hier völlig falsch bin. Ich habe versucht, die Verbindung manuell unter FOS \ UserBundle \ Doctrine \ UserManager. Im Folgenden ist der Konstruktor der Klasse

// 
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 einem Controller, können wir die folgende Methode verwenden, um die EM zu ‚Test‘ zu ändern

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

Dafür habe ich den Code wie folgt geändert zu Verwenden Sie EntityManager anstelle von 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(); 
} 

Meine App funktioniert ohne Fehler.

Von der Art, wie es mit dem Controller funktioniert, habe ich versucht, die Verbindung zu ändern, indem Sie einen Parameter zu dieser Zeile hinzufügen, aber es verwendet immer noch die Standardverbindung.

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

Was könnte ich hier noch vermissen?

+2

Ich glaube nicht, dass der FOSUserBundle dafür ausgelegt ist. Sie können die Funktionalität erweitern, indem Sie entweder zum Projekt selbst beitragen oder eine Verzweigung erstellen. Oder Sie könnten Ihr eigenes UserBundle mit Unterstützung für verschiedene Verbindungen schreiben. – Sgoettschkes

+1

@Sgoetttschkes: Ich stimme Ihnen vollkommen zu. Ich bin noch nicht in der Lage, eine Lösung/Workaround zu seinem Problem von mir zu finden, aber wenn ich es tue, werde ich es hier sicherlich teilen, wenn nicht durch eine Pull-Anfrage bei Github :) –

+1

Haben Sie den richtigen Objekt-Manager in die injiziert UserManager Klassenkonstruktor (der Test)? –

Antwort

11

Wie Sie sehen können, kann FOSUserBundle nur einen EntityManager haben. Sie können es von den Einstellungen 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 sehen.model_manager_name% in den Einstellungen als model_manager_name angegeben

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

So in den Konstruktor kommt die Instanz von EntityManager, die nicht den zweiten Parameter in der getRepository akzeptiert. Daher kann der Standard-FOSUserBundle nur mit einer Datenbank arbeiten.


Aber das ist nicht das Ende der Geschichte, es ist Symfony :) Wir Usermanager schreiben können, die verschiedenen DB-Verbindungen nutzen kann. In der Einstellung sehen Sie, dass fos_user.user_manager ein fos_user.user_manager.default ist. Wir finden es 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> 

wir diese Klasse überschreiben können, einen zusätzlichen Parameter hinzufügen, die bestimmen, welche Art von Verbindung, die Sie verwenden möchten. Außerdem können Sie mit ManagerFactory den gewünschten ObjectManager erhalten. Ich schrieb einfaches Beispiel für die beiden databeses (wenn Sie mehr Datenbanken benötigen Sie eine Fabrik für diesen Dienst schreiben)

definieren Ihre Dienste 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% 

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

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

Es tut mir leid, dass die Antwort so groß war. Wenn etwas bis zum Ende nicht klar ist, habe ich den Code auf github

1

FosUserBundle kann nicht mehr als einen Entity Manager haben.

Der einfachste Weg, um 2 Datenbanken zu verwenden, besteht darin, die 'checkLoginAction' des SecurityControllers zu überschreiben.

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

} 
Verwandte Themen