繁体   English   中英

Symfony 3.4 - 从命令切换到动态数据库

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM