多个FOSUserBundle的实体pipe理器

要在Symfony中使用基于URL的不同实体pipe理器/连接(如果相当容易)。 使用以下路由configuration

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

并从以下食谱;

如何使用多个实体pipe理器和连接

我的控制器看起来像这样;

 class UserController extends Controller { public function indexAction($connection) { $products = $this->get('doctrine') ->getRepository('AcmeStoreBundle:Product', $connection) ->findAll() ; .................. 

我将能够从不同的em /连接/数据库获取产品信息。

现在,如果我添加这样的东西到我的路由;

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

我怎样才能轻松使login使用连接定义在连接variables?

这个设置假定每个数据库都有自己的用户login信息(fos_user表)。

编辑:更新路由信息

EDIT2:

尽pipe如此,我还是使用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') 

为此,我将代码更改为使用EntityManager而不是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(); } 

我的应用程序工作正常,没有错误。

从它与控制器一起工作的方式来说,我试图通过向这一行添加一个参数来改变连接,但是它仍然使用默认的连接。

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

我还能在这里错过什么?

正如你所看到的,FOSUserBundle只能有一个EntityManager。 你可以从设置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中的第二个参数。 因此,标准的FOSUserBundle只能使用一个数据库。


但这不是故事的结尾,它是Symfony :)我们可以写出UserManager,它可以使用不同的数据库连接。 在设置中看到fos_user.user_manager是一个fos_user.user_manager.default。 我们在orm.xml中find它

 <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> 

我们可以覆盖这个类来添加一个额外的参数,这个参数将决定你想要使用什么types的连接。 进一步由ManagerFactory您可以得到所需的ObjectManager。 我为两个数据库写了一个简单的例子(如果你需要更多的数据库,你可以写这个服务的工厂)

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

和简单的testing

 /** * 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上

FosUserBundle不能拥有多个实体pipe理器。

我发现使用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); } } }