37 votos

Múltiples gerente de la entidad para el FOSUserBundle

El uso de diferentes Gerente de la Entidad / Relación basada en la URL en Symfony si bastante fácil. Con la siguiente configuración de enrutamiento

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

y a partir de la siguiente libro de cocina;

Cómo trabajar con Varios Gestores de Entidad y Conexiones

Mi controlador sería algo como esto;

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

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

y voy a ser capaz de recuperar la información de producto de diferentes em/conexión/base de datos.

Ahora, si puedo añadir algo como esto para mi enrutamiento;

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

¿Cómo puedo hacer fácilmente el inicio de sesión para utilizar la conexión como se define en la relación de la variable?

Esta configuración asumir cada base de datos tiene su propia cuenta de usuario, información de inicio de sesión (el fos_user tabla).

Edit: Actualizado la información de enrutamiento

Edit2:

Todavía estoy de nuevo con PHP/Symfony/Doctrina, aunque, así que por favor me perdone si estoy completamente equivocado aquí. Traté de configurar manualmente la conexión a FOS\UserBundle\Doctrine\UserManager. El siguiente es el constructor de la clase

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

En un controlador, se puede usar el siguiente método para cambiar el em 'pruebas'

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

Por eso he cambiado el código para el siguiente uso de EntityManager en lugar de 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();
}

Mi aplicación funciona bien con ningún error.

De la forma en la que funciona con el controlador, he intentado cambiar la conexión mediante la adición de un parámetro para esta línea de entonces, pero aún así el uso de la conexión predeterminada.

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

¿Qué más podía faltar aquí?

7voto

forgottenbas Puntos 5254

Como se puede ver, FOSUserBundle sólo puede tener un EntityManager. Se puede ver desde la configuración 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>

El parámetro %fos_user.model_manager_name% especificado en la configuración como model_manager_name

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

Así que en el constructor viene la instancia de EntityManager, que no acepta el segundo parámetro en el getRepository. Por lo tanto, el estándar de FOSUserBundle sólo puede trabajar con una base de datos.


Pero este no es el fin de la historia, es Symfony :) Podemos escribir UserManager, que se puede utilizar de base de datos diferentes conexiones. En la configuración a ver que fos_user.user_manager es un fos_user.user_manager.por defecto. La encontramos en la 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>

Podemos sustituir esta clase para añadir un parámetro adicional que va a determinar qué tipo de conexión que desea utilizar. Más por ManagerFactory usted puede conseguir el deseado ObjectManager. Escribí ejemplo sencillo para los dos databeses (si necesita más bases de datos, usted puede escribir su fábrica para este servicio)

definir sus servicios en los servicios.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%

Su gerente

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

y prueba simple

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

Siento que la respuesta era tan grande. Si algo no está claro para el final, pongo el código en github

0voto

Salestrip Puntos 41

FosUserBundle no es capaz de tener más de un administrador de la entidad.

La forma más fácil que he encontrado para el uso de 2 bases de datos, es para anular el 'checkLoginAction' de la 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);   
            }
        }

}

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X