Il mio progetto symfony2 ha un database principale e molti database figlio. Ogni database figlio viene creato per ogni utente, le credenziali del database sono memorizzate nel database principale. Quando l'utente esegue l'accesso, le credenziali del database specifiche dell'utente vengono recuperate dal database principale e la connessione al database figlio dovrebbe essere stabilita idealmente. Ho cercato su google per lo stesso, e mi sono imbattuto una serie di soluzioni e, infine, ha fatto la seguente:Connessione dinamica al database symfony2
#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
ho creato una connessione predefinita per la connessione al database principale e una connessione a vuoto per il database del bambino, allo stesso modo io creato gestori di entità. Poi ho creato listener di eventi di default e ha aggiunto il seguente codice al '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');
....
}
Il codice di cui sopra imposta i parametri del database bambino e reimposta il gestore di entità dynamic_em.
Quando eseguo le operazioni seguenti in alcuni controller, funziona correttamente e i dati vengono recuperati dal database figlio.
$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine
Ma, quando io uso contesto di protezione come si vede nel codice seguente, ottengo un errore 'NO DATABASE SELEZIONATO'.
$securityContext = $this->container->get('security.context');
$loggedinUserid = $securityContext->getToken()->getUser()->getId();
Come posso impostare la connessione al database in modo dinamico e utilizzare contesto di sicurezza come bene?
UPDATE: -
Dopo molto tempo speso per tentativi ed errori, e googling intorno, mi sono reso conto che security.context
è impostato prima dell'esecuzione di onKernelRequest
. Ora la domanda è in che modo inserire i dettagli della connessione al database in security.context e dove iniettare?
Abbiamo bisogno di arrivare a un punto in cui il contesto DBAL e sicurezza è impostato e il token di sicurezza viene creato, e possiamo manipolare i dettagli della connessione al database.
Quindi, come dichiarato dal seguente link, ho apportato delle modifiche al mio codice, questo è esattamente quello che vorrei fare. http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413
che mi lascia il seguente codice aggiungi al mio progetto:
#config.yml //remains unchanged, similar to above code
un passaggio compilatore è creato come segue:
// 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());
}
}
Il pass compilatore è la seguente:
# 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]);
}
}
}
Il codice classe driver è il seguente:
Il codice sopra riportato è stato aggiunto al mio progetto e presumo che questo sia il vero problema del mio problema.
Ma ora ottengo il seguente errore:
ServiceCircularReferenceException: Circular reference detected for service "security.context", path: "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".
Come, posso ottenere il mio codice per funzionare? Scommetto che sto facendo qualcosa di sbagliato qui e apprezzerei ogni suggerimento e aiuto.
è il codice utilizzando il 'security.context' corse * prima * l'evento' onKernelRequest'? alcune dipendenze di security.context devono utilizzare 'dynamic_em' – arnaud576875
Il codice security.context viene aggiunto alla stessa azione in cui viene aggiunto il codice getRespository. Quindi suppongo, che onKernelRequest sia eseguito prima di security.context. –
@John L'ho appena testato con Sf 2.3.2 e la tua prima soluzione funziona per me. Ho usato un sottoscrittore di eventi per modificare l'oggetto di connessione. Il controller in seguito recuperò una riga dal database "slave" e caricò l'utente attualmente connesso (usato 'FOSUserBundle'). Il contesto di sicurezza viene popolato durante la fase di dispacciamento dell'evento e, a prima vista, ho pensato che si trattasse semplicemente di cambiare le priorità degli ascoltatori. Se non l'hai già capito, forse potresti dirmi quale versione di Symfony stai usando? – gilden