[英]Symfony 3.4 - switch to dynamic database from command
最近我一直忙于将一个旧项目从 Symfony 2.7 升级到 3.4。 该项目使用自定义多租户设置,并依靠 EventListener 在基于子域的数据库之间切换。 这是可行的,因为我可以在我的services.yml
文件中定义该 EventListener 的优先级,以便在任何其他服务之前加载它。 通过这样做,我的所有其他代码都使用了侦听器设置的数据库。
current_site_listener:
class: AppBundle\Event\Listener\CurrentSiteListener
arguments: ["@doctrine.dbal.default_connection", "%base_host%"]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 10 }
到目前为止一切顺利,但不幸的是,该逻辑不适用于我的任何租户特定命令。 这些命令使用参数选项来定义应该使用哪个数据库。 由于我无法为这些命令定义优先级,因此其他服务已经加载,我无法从命令中覆盖数据库连接。 下面的代码是我在命令和 EventListener 中使用的代码,并且在后者中运行良好(并且在命令中的 Symfony 2.7 中也运行良好)。
$connectionParams = array(
'dbname' => $client['database_name'],
'user' => $client['database_login'],
'password' => $client['database_password'],
'host' => $client['database_host'],
'driver' => 'pdo_mysql',
'charset' => 'UTF8'
);
/** @var Connection $connection */
$connection = $this->container->get('doctrine.dbal.default_connection');
if ($connection->isConnected()) {
$connection->close();
}
$connection->__construct(
$connectionParams, $connection->getDriver(), $connection->getConfiguration(),
$connection->getEventManager()
);
上面的代码在 Symfony 2.7 中运行良好,但我的猜测是在该版本和 Symfony 3.4 之间,服务的工作方式发生了一些变化,我无法再用我当前的数据库连接覆盖它们。 我在迁移文档中没有遇到有关此问题的任何更改,因此对如何解决此问题一无所知。 我希望这里有人对如何解决这个问题有任何想法或建议。
在此先感谢和亲切的问候,
凯文
问题是您无法在运行时更改连接参数,我之前在 Symfony 3.4 中也有过相同的情况,我通过扩展connection
以支持运行时更改参数来解决此问题。
我终于为此创建了一个 Symfony 包
https://github.com/RamyHakam/doctrine-db-switcher-bundle但它只适用于 Symfony 4+
但是,我可以与您分享它的想法,它应该适用于 Symfony 3.4
你应该像这个例子一样配置你的连接
connections:
default:
driver: ''
charset:
host: ''
port: ''
dbname: ''
user: ''
password: ''
wrapper_class: App\Doctrine\DBAL\TenantConnection
wrapper_class is your new conneciton
Class 示例:
namespace App\Doctrine\DBAL;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Event;
class TenantConnection extends Connection
{
/** @var mixed */
protected $params = [];
/** @var bool */
protected $isConnected = false;
/** @var bool */
protected $autoCommit = true;
/**
* TenantConnection constructor.
*
* @param $params
* @param Driver $driver
* @param Configuration|null $config
* @param EventManager|null $eventManager
* @throws \Doctrine\DBAL\DBALException
*/
public function __construct($params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null)
{
$this->params = $params;
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* @return bool
*/
public function connect()
{
if ($this->isConnected) {
return false;
}
$driverOptions = $this->params['driverOptions'] ?? [];
$user = $this->params['user'] ?? null;
$password = $this->params['password'] ?? null;
$this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions);
$this->isConnected = true;
if ($this->autoCommit === false) {
$this->beginTransaction();
}
if ($this->_eventManager->hasListeners(Events::postConnect)) {
$eventArgs = new Event\ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
}
return true;
}
/**
* @param string $dbName
* @param string $dbUser
* @param string $dbPassword
*/
public function changeParams(string $dbName, string $dbUser, string $dbPassword)
{
$this->params['dbname'] = $dbName;
$this->params['user'] = $dbUser;
$this->params['password'] = $dbPassword;
}
public function reconnect()
{
if ($this->isConnected) {
$this->close();
}
$this->connect();
}
/**
* @return mixed|mixed[]
*/
public function getParams()
{
return $this->params;
}
public function close()
{
$this->_conn = null;
$this->isConnected = false;
}
然后在您的事件侦听器中,您可以将连接切换到另一个数据库
例子:
namespace App\EventListener;
use App\Doctrine\DBAL\TenantConnection;
use App\Main\Domain\Model\Tenant;
use App\Main\Infrastructure\Persistence\Doctrine\Repository\TenantRepository;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class RequestListener implements EventSubscriberInterface
{
/**
* @var ContainerInterface
*/
private $container;
/**
* @var SessionInterface
*/
private $session;
/**
* @var TenantRepository
*/
private $tenantRepository;
public function __construct(ContainerInterface $container,TenantRepository $tenantRepository)
{
$this->container = $container;
$this->tenantRepository = $tenantRepository;
}
public static function getSubscribedEvents()
{
return [
RequestEvent::class => 'onKernelRequest'
];
}
public function onKernelRequest( RequestEvent $event)
{
$userAgent = $event->getRequest()->headers->get('user_identifier');
/**
* @var TenantConnection $tenantConnection
*/
$tenantConnection = $this->container->get('doctrine')->getConnection('tenant');
/**@var Tenant $tenant */
$tenants = $this->tenantRepository->findAll();
$tenant = $tenants[1];
$tenantConnection->changeParams($tenant->getDbName(), $tenant->getDbUserName(), $tenant->getDbPassword());
$tenantConnection->reconnect();
}
}
[1]: https://github.com/RamyHakam/doctrine-db-switcher-bundle
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.