【问题标题】:Symfony 3.4 - switch to dynamic database from commandSymfony 3.4 - 从命令切换到动态数据库
【发布时间】:2020-08-03 04:29:31
【问题描述】:

最近我一直忙于将一个旧项目从 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 之间,服务的工作方式发生了一些变化,我无法再用我当前的数据库连接覆盖它们。我在迁移文档中没有遇到有关此问题的任何更改,因此对如何解决此问题一无所知。我希望这里有人对如何解决这个问题有任何想法或建议。

在此先感谢您的问候,

凯文

【问题讨论】:

  • 您在执行命令时是否有任何错误?
  • @gdequippe,不仅仅是我的初始连接在该命令调用的其他服务中使用。

标签: php symfony command event-listener


【解决方案1】:

问题是你不能在运行时改变连接参数, 我之前在 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

类示例:


    
    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

【讨论】:

    猜你喜欢
    • 2018-07-23
    • 2019-04-02
    • 2012-04-12
    • 2019-08-10
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 2018-11-04
    • 2021-03-10
    相关资源
    最近更新 更多