Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If you use <code>ConnectionFactory</code>, your event subscribers attached to the connection will stop working, for example stofDoctrineExtensions. </p> <p>Here is my method. I have as with <code>ConnectionFactory</code> have empty connection and EntityManager. While working I just replace connection configuration by Reflections. Works on SF 2.0.10 ;)</p> <pre class="lang-php prettyprint-override"><code>class YourService extends ContainerAware { public function switchDatabase($dbName, $dbUser, $dbPass) { $connection = $this-&gt;container-&gt;get(sprintf('doctrine.dbal.%s_connection', 'dynamic_conn')); $connection-&gt;close(); $refConn = new \ReflectionObject($connection); $refParams = $refConn-&gt;getProperty('_params'); $refParams-&gt;setAccessible('public'); //we have to change it for a moment $params = $refParams-&gt;getValue($connection); $params['dbname'] = $dbName; $params['user'] = $dbUser; $params['password'] = $dbPass; $refParams-&gt;setAccessible('private'); $refParams-&gt;setValue($connection, $params); $this-&gt;container-&gt;get('doctrine')-&gt;resetEntityManager('dynamic_manager'); // for sure (unless you like broken transactions) } } </code></pre> <p><strong>UPDATE</strong>:</p> <p>More elegant solution for doctrine 2.2 / sf 2.3 (without relection), created for php5.4 (I love new array initializer :D) We can use doctrine feature called connection wrapper, see <a href="http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/portability.html" rel="noreferrer">http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/portability.html</a></p> <p>This example use session service for temporary storing connection details.</p> <p>At first we have to create special connection wrapper:</p> <pre class="lang-php prettyprint-override"><code>namespace w3des\DoctrineBundle\Connection; use Doctrine\DBAL\Connection; use Symfony\Component\HttpFoundation\Session\Session; use Doctrine\Common\EventManager; use Doctrine\DBAL\Events; use Doctrine\DBAL\Event\ConnectionEventArgs; /* * @author Dawid zulus Pakula [zulus@w3des.net] */ class ConnectionWrapper extends Connection { const SESSION_ACTIVE_DYNAMIC_CONN = 'active_dynamic_conn'; /** * @var Session */ private $session; /** * @var bool */ private $_isConnected = false; /** * @param Session $sess */ public function setSession(Session $sess) { $this-&gt;session = $sess; } public function forceSwitch($dbName, $dbUser, $dbPassword) { if ($this-&gt;session-&gt;has(self::SESSION_ACTIVE_DYNAMIC_CONN)) { $current = $this-&gt;session-&gt;get(self::SESSION_ACTIVE_DYNAMIC_CONN); if ($current[0] === $dbName) { return; } } $this-&gt;session-&gt;set(self::SESSION_ACTIVE_DYNAMIC_CONN, [ $dbName, $dbUser, $dbPass ]); if ($this-&gt;isConnected()) { $this-&gt;close(); } } /** * {@inheritDoc} */ public function connect() { if (! $this-&gt;session-&gt;has(self::SESSION_ACTIVE_DYNAMIC_CONN)) { throw new \InvalidArgumentException('You have to inject into valid context first'); } if ($this-&gt;isConnected()) { return true; } $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); $params = $this-&gt;getParams(); $realParams = $this-&gt;session-&gt;get(self::SESSION_ACTIVE_DYNAMIC_CONN); $params['dbname'] = $realParams[0]; $params['user'] = $realParams[1]; $params['password'] = $realParams[2]; $this-&gt;_conn = $this-&gt;_driver-&gt;connect($params, $params['user'], $params['password'], $driverOptions); if ($this-&gt;_eventManager-&gt;hasListeners(Events::postConnect)) { $eventArgs = new ConnectionEventArgs($this); $this-&gt;_eventManager-&gt;dispatchEvent(Events::postConnect, $eventArgs); } $this-&gt;_isConnected = true; return true; } /** * {@inheritDoc} */ public function isConnected() { return $this-&gt;_isConnected; } /** * {@inheritDoc} */ public function close() { if ($this-&gt;isConnected()) { parent::close(); $this-&gt;_isConnected = false; } } } </code></pre> <p>Next register it in your doctrine configuration:</p> <pre class="lang-yaml prettyprint-override"><code>… connections: dynamic: driver: %database_driver% host: %database_host% port: %database_port% dbname: 'empty_database' charset: UTF8 wrapper_class: 'w3des\DoctrineBundle\Connection\ConnectionWrapper' </code></pre> <p>And our ConnectionWrapper is properly registered. Now session injection.</p> <p>First create special CompilerPass class:</p> <pre class="lang-php prettyprint-override"><code>namespace w3des\DoctrineBundle\DependencyInjection\CompilerPass; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; class ConnectionCompilerPass implements CompilerPassInterface { /** * {@inheritDoc} */ public function process(ContainerBuilder $container) { $connection = $container -&gt;getDefinition('doctrine.dbal.dynamic_connection') -&gt;addMethodCall('setSession', [ new Reference('session') ]); } } </code></pre> <p>And we record our new compiler class in *Bundle class:</p> <pre class="lang-php prettyprint-override"><code>public function build(ContainerBuilder $container) { parent::build($container); $container-&gt;addCompilerPass(new ConnectionCompilerPass()); } </code></pre> <p>And that its all!</p> <p>Connection will be created on demand, based on session properties.</p> <p>To switch database, just use:</p> <pre class="lang-php prettyprint-override"><code>$this-&gt;get('doctrine.dbal.dynamic_connection')-&gt;forceSwitch($dbname, $dbuser, $dbpass); </code></pre> <p><strong>Advantages</strong></p> <ol> <li>No more reflection</li> <li>Creation on demand</li> <li>Elegant and powerfull</li> </ol> <p><strong>Disadvantages</strong></p> <ol> <li>You have to manualy cleanup your entity manager, or create special doctrine event for this</li> <li>Much more code</li> </ol>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload