Conexión de base de datos dinámica symfony2
Mi proyecto symfony2 tiene una base de datos principal y muchas bases de datos secundarias. Cada base de datos secundaria se crea para cada usuario, las credenciales de la base de datos se almacenan en la base de datos principal. Cuando el usuario inicia sesión, las credenciales específicas de la base de datos del usuario se obtienen de la base de datos principal y, idealmente, se debe establecer la conexión de la base de datos secundaria. Busqué en Google lo mismo, encontré varias soluciones y finalmente hice lo siguiente:
#config.yml
doctrine:
dbal:
default_connection: default
connections:
default:
dbname: maindb
user: root
password: null
host: localhost
dynamic_conn:
dbname: ~
user: ~
password: ~
host: localhost
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
auto_mapping: true
dynamic_em:
connection: dynamic_conn
auto_mapping: true
Creé una conexión predeterminada para conectarme a la base de datos principal y una conexión vacía para la base de datos secundaria, de manera similar, creé administradores de entidades. Luego creé la escucha de eventos predeterminada y agregué el siguiente código a 'onKernelRequest':
public function onKernelRequest(GetResponseEvent $event) //works like preDispatch in Zend
{
//code to get db credentials from master database and stored in varaiables
....
$connection = $this->container->get(sprintf('doctrine.dbal.%s_connection', 'dynamic_conn'));
$connection->close();
$refConn = new \ReflectionObject($connection);
$refParams = $refConn->getProperty('_params');
$refParams->setAccessible('public'); //we have to change it for a moment
$params = $refParams->getValue($connection);
$params['dbname'] = $dbName;
$params['user'] = $dbUser;
$params['password'] = $dbPass;
$refParams->setAccessible('private');
$refParams->setValue($connection, $params);
$this->container->get('doctrine')->resetEntityManager('dynamic_em');
....
}
El código anterior establece los parámetros de la base de datos secundaria y restablece el administrador de entidad dynamic_em.
Cuando hago lo siguiente en algún controlador, funciona bien y los datos se obtienen de la base de datos secundaria.
$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine
Pero, cuando uso el contexto de seguridad como se ve en el siguiente código, aparece el mensaje "NO SE HA SELECCIONADO BASE DE DATOS".
$securityContext = $this->container->get('security.context');
$loggedinUserid = $securityContext->getToken()->getUser()->getId();
¿Cómo puedo configurar la conexión de base de datos dinámicamente y usar el contexto de seguridad también?
ACTUALIZAR:-
Después de pasar mucho tiempo en prueba y error, y buscar en Google, me di cuenta de quesecurity.context
se establece antes de la ejecución deonKernelRequest
. Ahora la pregunta escómo para insertar los detalles de conexión de la base de datos en el security.context, ydónde ¿inyectar?
Necesitamos llegar a un punto donde se establezca el contexto de seguridad y DBAL y se cree el token de seguridad, y podemos manipular los detalles de conexión de la base de datos.
Por lo tanto, como dijo la persona en el siguiente enlace, hice cambios en mi código, ya que eso es exactamente lo que querría hacer.http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413
Eso me deja el siguiente código agregar a mi proyecto:
#config.yml //remains unchanged, similar to above code
Un pase de compilación se crea de la siguiente manera:
// src/Blogger/BlogBundle/BloggerBlogBundle.php
namespace Blogger\BlogBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Blogger\BlogBundle\DependencyInjection\Compiler\CustomCompilerPass;
class BloggerBlogBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CustomCompilerPass());
}
}
El pase del compilador es el siguiente:
# src/Blogger/BlogBundle/DependencyInjection/Compiler/CustomCompilerPass.php
class CustomCompilerPassimplements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$connection_service = 'doctrine.dbal.dynamic_conn_connection';
if ($container->hasDefinition($connection_service))
{
$def = $container->getDefinition($connection_service);
$args = $def->getArguments();
$args[0]['driverClass'] = 'Blogger\BlogBundle\UserDependentMySqlDriver';
$args[0]['driverOptions'][] = array(new Reference('security.context'));
$def->replaceArgument(0, $args[0]);
}
}
}
El código de clase del controlador es el siguiente:
# src/Blogger/BlogBundle/UserDependentMySqlDriver.php
use Doctrine\DBAL\Driver\PDOMySql\Driver;
class UserDependentMySqlDriver extends Driver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
$dbname = ..... //store database name in variable
$params['dbname'] = $dbname;
return parent::connect($params, $username, $password, array());
}
}
El código anterior se agregó a mi proyecto y asumo que este es el trabajo real para mi problema.
Pero ahora me sale el siguiente error:
ServiceCircularReferenceException: referencia circular detectada para el servicio "security.context", ruta: "profiler_listener -> profiler -> security.context -> security.authentication.manager -> fos_user.user_provider.username_email -> fos_user.user_manager -> doctrine.orm. dynamic_manager_entity_manager -> doctrine.dbal.dynamic_conn_connection ".
¿Cómo puedo hacer que mi código funcione? Apuesto a que estoy haciendo algo mal aquí y agradecería cualquier consejo y ayuda.