Symfony 2: multiple og dynamisk database tilkobling

stemmer
52

Jeg er ganske ny på SF2 og jeg lurte på hvordan jeg kunne klare forbindelser til severals databaser i ONE bunt. For øyeblikket har jeg denne løsningen - som fungerer fint - men jeg vet ikke om det er den rette måten å gjøre det ....

i myBundle \ Ressource \ config \ config.yml:

doctrine:
dbal:
    default_connection:       default
    connections:
        default:
            dbname:           SERVER
            user:             root
            password:         null
            host:             localhost
        client:
            dbname:           CLIENT_134
            user:             root
            password:         null
            host:             localhost
orm:
    default_entity_manager:   default
    entity_managers:
        default:
            connection:       default
            mappings:
                MyBundle: ~
        client:
            connection:       client
            mappings:
                MyBundle: ~

Og så, for å bytte til en av BD eller det andre, jeg gjør:

$O_ressource=  $this->get('doctrine')->getEntityManager('client');
$O_ressource=  $this->get('doctrine')->getEntityManager('default');

Så folkens, tror du det er en god måte å håndtere dette?

Og min andre spørsmålet er:

hvordan sette opp dynamisk database tilkobling? Jeg mener jeg har 100 databaser i systemet mitt, og jeg kan ikke sette alle dem i min config.yml fil. Så jeg vil gjerne være i stand til å endre database på fly.

Takk for hjelpen!

Publisert på 20/06/2011 klokken 09:24
kilden bruker
På andre språk...                            


3 svar

stemmer
50

Hvis du bruker ConnectionFactory, vil hendelses abonnenter knyttet til sammenhengen slutte å virke, for eksempel stofDoctrineExtensions.

Her er min metode. Jeg har så med ConnectionFactoryhar tom tilkobling og EntityManager. Mens han jobbet jeg bare erstatte forbindelse konfigurasjon av refleksjoner. Fungerer på SF 2.0.10;)

class YourService extends ContainerAware
{ 

  public function switchDatabase($dbName, $dbUser, $dbPass) 
  {
    $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_manager'); // for sure (unless you like broken transactions)
  }
}

UPDATE :

Mer elegant løsning for læren 2.2 / sf 2,3 (uten relection), opprettet for php5.4 (jeg elsker nye utvalget initializer: D) Vi kan bruke læren funksjon kalt forbindelse wrapper, se http://docs.doctrine-project.org/ prosjekter / doktrine-dbal / no / nyeste / referanse / portability.html

Dette eksempelet bruker session tjeneste for midlertidig lagring tilkoblingsdetaljer.

Ved første må vi lage spesiell forbindelse wrapper:

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->session = $sess;
}

public function forceSwitch($dbName, $dbUser, $dbPassword)
{
    if ($this->session->has(self::SESSION_ACTIVE_DYNAMIC_CONN)) {
        $current = $this->session->get(self::SESSION_ACTIVE_DYNAMIC_CONN);
        if ($current[0] === $dbName) {
            return;
        }
    }

    $this->session->set(self::SESSION_ACTIVE_DYNAMIC_CONN, [
        $dbName,
        $dbUser,
        $dbPass
    ]);

    if ($this->isConnected()) {
        $this->close();
    }
}

/**
 * {@inheritDoc}
 */
public function connect()
{
    if (! $this->session->has(self::SESSION_ACTIVE_DYNAMIC_CONN)) {
        throw new \InvalidArgumentException('You have to inject into valid context first');
    }
    if ($this->isConnected()) {
        return true;
    }

    $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array();

    $params = $this->getParams();
    $realParams = $this->session->get(self::SESSION_ACTIVE_DYNAMIC_CONN);
    $params['dbname'] = $realParams[0];
    $params['user'] = $realParams[1];
    $params['password'] = $realParams[2];

    $this->_conn = $this->_driver->connect($params, $params['user'], $params['password'], $driverOptions);

    if ($this->_eventManager->hasListeners(Events::postConnect)) {
        $eventArgs = new ConnectionEventArgs($this);
        $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
    }

    $this->_isConnected = true;

    return true;
}

/**
 * {@inheritDoc}
 */
public function isConnected()
{
    return $this->_isConnected;
}

/**
 * {@inheritDoc}
 */
public function close()
{
    if ($this->isConnected()) {
        parent::close();
        $this->_isConnected = false;
    }
}
}

Neste registrere den i læren konfigurasjon:



connections:
  dynamic:
    driver:   %database_driver%
    host:     %database_host%
    port:     %database_port%
    dbname:   'empty_database'
    charset:  UTF8
    wrapper_class: 'w3des\DoctrineBundle\Connection\ConnectionWrapper'

Og vår ConnectionWrapper er riktig registrert. Nå session injeksjon.

Først lage spesielle CompilerPass klasse:

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
    ->getDefinition('doctrine.dbal.dynamic_connection')
    ->addMethodCall('setSession', [
        new Reference('session')
    ]);
}
}

Og vi registrerer vår nye kompilatoren klasse i * Bundle klasse:

public function build(ContainerBuilder $container)
{
    parent::build($container);
    $container->addCompilerPass(new ConnectionCompilerPass());
}

Og at det er alt!

Forbindelsen vil bli opprettet etter behov, basert på økt egenskaper.

Å bytte database, bare bruk:

$this->get('doctrine.dbal.dynamic_connection')->forceSwitch($dbname, $dbuser, $dbpass);

Fordeler

  1. Ikke mer refleksjon
  2. Creation on demand
  3. Elegant og kraftig

ulemper

  1. Må du manuelt opprydding din enhet manager, eller lage spesielle doktrine hendelser denne
  2. Mye mer kode
Svarte 15/02/2012 kl. 10:45
kilden bruker

stemmer
23

Du kan se inn Symfony\Bundle\DoctrineBundle\ConnectionFactory, ved hjelp av container-tjenesten doctrine.dbal.connection_factory:

$connectionFactory = $this->container->get('doctrine.dbal.connection_factory');
$connection = $connectionFactory->createConnection(array(
    'driver' => 'pdo_mysql',
    'user' => 'root',
    'password' => '',
    'host' => 'localhost',
    'dbname' => 'foo_database',
));

Det er bare en rask eksempel, men det bør komme i gang.

Svarte 21/06/2011 kl. 16:38
kilden bruker

stemmer
0

Jeg kjøre inn det samme som ønsker å ha ulike databaser med samme skjema for hver klient. Siden symfony 2,3, etter nedgraderingen av metoden resetEntityManager, jeg la merke til at koden kjøres godt uten å lukke tilkoblingen og uten å tilbakestille (gamle Entity) Manager.

dette er min nåværende arbeids kode:

public function switchDatabase($dbName, $dbUser, $dbPass) {
    $connection = $this->container->get(sprintf('doctrine.dbal.%s_connection', 'dynamic_conn'));

    $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);
}
Svarte 21/06/2013 kl. 09:36
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more