web-dev-qa-db-ja.com

FOSUserBundleの複数エンティティマネージャー

かなり簡単な場合、SymfonyのURLに基​​づいて異なるエンティティマネージャー/接続を使用する。次のルーティング構成で

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 }

loginconnection変数で定義されている接続を使用するように簡単に作成するにはどうすればよいですか?

この設定では、各データベースに独自のユーザーログイン情報(fos_userテーブル)があることを前提としています。

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

Edit2:

私はまだ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');

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

40

ご覧のとおり、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>

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

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

したがって、コンストラクターには、getRepositoryの2番目のパラメーターを受け入れないEntityManagerのインスタンスが含まれます。したがって、標準の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 に置きます

12
Alexey B.

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

}
1
Jaycreation