40

かなり簡単な場合、Symfony で URL に基づいて異なる Entity Manager / Connection を使用する。次のルーティング構成で

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

および次のクックブックから。

複数のエンティティ マネージャーと接続を使用する方法

私のコントローラーは次のようになります。

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

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

異なる em/connection/database から製品情報を取得できるようになります。

さて、このようなものをルーティングに追加すると;

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

接続変数で定義されている接続を使用するようにログインを簡単に行うにはどうすればよいですか?

このセットアップでは、各データベースが独自のユーザー ログイン情報 (fos_user テーブル) を持っていることを前提としています。

編集:ルーティング情報を更新しました

編集2:

私はまだ PHP/Symfony/Doctrine に慣れていないので、ここで完全に間違っている場合はご容赦ください。FOS\UserBundle\Doctrine\UserManagerで接続を手動で設定しようとしました。以下はクラスのコンストラクタです

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

コントローラーでは、次のメソッドを使用して em を「testing」に変更できます。

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

そのために、コードを次のように変更して、ObjectManager の代わりに EntityManager を使用しました。

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

私のアプリはエラーなく正常に動作します。

コントローラーでの動作から、この行にパラメーターを追加して接続を変更しようとしましたが、まだデフォルトの接続を使用しています。

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

ここで他に何が欠けている可能性がありますか?

4

2 に答える 2

12

ご覧のとおり、FOSUserBundle は EntityManager を 1 つだけ持つことができます。設定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>

パラメータ %fos_user.model_manager_name% が設定で model_manager_name として指定されました

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

そのため、コンストラクターには EntityManager のインスタンスが入りますが、これは getRepository の 2 番目のパラメーターを受け入れません。したがって、標準の FOSUserBundle は 1 つのデータベースでのみ機能します。


しかし、これで話は終わりではありません。それは Symfony です :) さまざまなデータベース接続を使用できる UserManager を書き出すことができます。設定で、fos_user.user_manager が fos_user.user_manager.default であることを確認します。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>

このクラスをオーバーライドして、使用する接続の種類を決定するパラメーターを追加できます。さらに ManagerFactory によって、目的の ObjectManager を取得できます。2 つのデータベースの簡単な例を書きました (さらに多くのデータベースが必要な場合は、このサービスのファクトリを作成できます)。

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%

あなたのマネージャー

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

と簡単なテスト

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

回答が大きすぎてすみません。最後まで不明な点があれば、コードをgithubに置きます

于 2013-04-28T10:39:26.933 に答える
1

FosUserBundle は複数のエンティティ マネージャを持つことができません。

2 つのデータベースを使用する最も簡単な方法は、SecurityController の「checkLoginAction」をオーバーライドすることです。

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

}
于 2013-11-05T10:29:41.570 に答える