В моем проекте symfony2 есть основная база данных и множество дочерних баз данных. Каждая дочерняя база данных создается для каждого пользователя, учетные данные базы данных хранятся в основной базе данных. Когда пользователь входит в систему, пользовательские учетные данные для базы данных извлекаются из основной базы данных и идеальное соединение с дочерней базой. Я тоже искал Google, и я пришел к ряду решений и, наконец, сделал следующее:
#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
Я создал соединение по умолчанию для подключения к основной базе данных и пустое соединение для дочерней базы данных, аналогично я создал менеджеров сущностей. Затем я создал прослушиватель событий по умолчанию и добавил следующий код в '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');
....
}
Приведенный выше код устанавливает параметры дочерней базы данных и сбрасывает диспетчер сущности dynamic_em.
Когда я делаю следующее на каком-то контроллере, он отлично работает и данные извлекаются из дочерней базы данных.
$getblog= $em->getRepository('BloggerBlogBundle:Blog')->findById($id); //uses doctrine
Но когда я использую контекст безопасности, как показано в следующем коде, я получаю сообщение об ошибке "NO DATABASE SELECTED".
$securityContext = $this->container->get('security.context');
$loggedinUserid = $securityContext->getToken()->getUser()->getId();
Как установить динамическое подключение к базе данных и использовать контекст безопасности?
UPDATE: -
После долгого времени, затраченного на проб и ошибок, и поиска в Google, я понял, что security.context
устанавливается перед выполнением onKernelRequest
. Теперь вопрос как ввести данные подключения к базе данных в файл security.context и , где, чтобы ввести?
Нам нужно дойти до точки, в которой установлен DBAL и контекст безопасности, и маркер безопасности создается, и мы можем манипулировать деталями подключения к базе данных.
Следовательно, как сказал человек в следующей ссылке, я вносил изменения в свой код, так как это именно то, что я хотел бы сделать. http://forum.symfony-project.org/viewtopic.php?t=37398&p=124413
Это оставляет мне следующий код, добавляемый в мой проект:
#config.yml //remains unchanged, similar to above code
Протокол компилятора создается следующим образом:
// 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());
}
}
Протокол компилятора выглядит следующим образом:
# 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]);
}
}
}
Код класса драйвера выглядит следующим образом:
# 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());
}
}
Вышеприведенный код был добавлен в мой проект, и я предполагаю, что это фактическая работа для моей проблемы.
Но теперь я получаю следующую ошибку:
ServiceCircularReferenceException: обнаружена циркулярная ссылка для service "security.context", путь: "profiler_listener → профайлер → 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".
Как я могу заставить мой код работать? Готов поспорить, что я делаю что-то неправильно здесь, и я был бы признателен за любые подсказки и помощь.