Doctrine 2 erweitern – Lesende und Schreibende Queries auf unterschiedliche Server

Ich beschäftige mich seit kurzem mit Doctrine 2 und setze es aktuell in einem Projekt ein. Jetzt kam heraus, dass hier wahrscheinlich Master-Slave-Replikation zum Einsatz kommt und die Applikation entscheiden muss, auf welchen Server welche Anfragen gehen. „Das kann Doctrine 2 von Haus aus“ … Berühmte letzte Worte. 😉 Nein, kann es nicht. Aber nach eine bisle Recherche kam mir die Idee, wie ich es hinbekommen kann.

Config:

Am Treiber selber habe ich nichts geändert, lediglich die Wrapper-Klasse, habe ich durch meine überschrieben

[code]

production:
driver: pdo_mysql
wrapperClass: \MyApplication\DBAL\Connection
master:
user: root
password:
dbname: deal
host: localhost
slave:
user: root
password:
dbname: deal
host: localhost
staging:
_extends: production
test:
_extends: staging
development:
_extends: test

[/code]

Connection:

Überschreibt die Standard-Connection und parsed die Statements.

[code]

<?php
namespace MyApplication\DBAL;

use Doctrine\DBAL\Events,
Doctrine\DBAL\Event,
Doctrine\DBAL\Driver,
Doctrine\DBAL\Configuration,
Doctrine\Common\EventManager;

class Connection extends \Doctrine\DBAL\Connection
{
/* CONSTANTS */
// type of a query
const QUERY_TYPE_READ   = ‚read‘;
const QUERY_TYPE_WRITE  = ‚write‘;

/* PROPERTIES */
/**
* The wrapped driver connection for writing.
*
* @var Doctrine\DBAL\Driver\Connection
*/
private $_masterConnection;

/**
* The wrapped driver connection for reading.
*
* @var Doctrine\DBAL\Driver\Connection
*/
private $_slaveConnection;

/**
* The parameters used during creation of the Connection instance.
*
* @var array
*/
private $_params = array();

/* METHODS */
/**
* overwritten constructor for setting my own parameters
*
* @param array $params  The connection parameters.
* @param Driver $driver
* @param Configuration $config
* @param EventManager $eventManager
*/
public function __construct(array $params, Driver $driver, Configuration $config = null,
EventManager $eventManager = null)
{
parent::__construct($params, $driver, $config, $eventManager);

$this->_params = $params;
}

/**
* Establishes the connection with the database.
*
* @return boolean TRUE if the connection was successfully established, FALSE if
*                 the connection is already open.
*/
public function connect()
{
$returnValue = false;

if (!$this->_isConnected) {
$this->_masterConnection    = $this->_getConnection($this->_params[‚master‘]);
$this->_slaveConnection     = $this->_getConnection($this->_params[’slave‘]);

$this->_conn                = $this->_slaveConnection;

$this->_isConnected         = true;

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

$returnValue = true;
}

return $returnValue;
}

/**
*
* @param array $params
* @return Doctrine\DBAL\Driver\Connection
*/
private function _getConnection(array $params) {
$driverOptions  = isset($params[‚driverOptions‘]) ? $params[‚driverOptions‘] : array();
$user           = isset($params[‚user‘]) ? $params[‚user‘] : null;
$password       = isset($params[‚user‘]) ? $params[‚password‘] : null;

return $this->_driver->connect($params, $user, $password, $driverOptions);
}

/**
* Closes the connection.
*
* @return void
*/
public function close()
{
parent::close();
unset($this->_masterConnection);
unset($this->_slaveConnection);
}

/**
* Executes an, optionally parameterized, SQL query.
*
* If the query is parameterized, a prepared statement is used.
* If an SQLLogger is configured, the execution is logged.
*
* @param string $query The SQL query to execute.
* @param array $params The parameters to bind to the query, if any.
* @return Doctrine\DBAL\Driver\Statement The executed statement.
* @internal PERF: Directly prepares a driver statement, not a wrapper.
*/
public function executeQuery($query, array $params = array(), $types = array())
{
$this->connect();
$this->_switchConnectionByQuery($query);

parent::executeQuery($query, $params, $types);
}

/**
* get type of a query
* @param string $query
* @return string
*/
private function _getQueryType($query) {
$returnValue = self::QUERY_TYPE_READ;

$startToken = strtolower(reset(explode(‚ ‚, trim($query))));
switch($startToken) {
case ‚delete‘:
case ‚insert‘:
case ‚update‘:
$returnValue = self::QUERY_TYPE_WRITE;
break;
}

return $returnValue;
}

/**
* switches the connection by query
* @param string $query
*/
private function _switchConnectionByQuery($query) {
if ($this->_getQueryType($query) == self::QUERY_TYPE_WRITE) {
$this->_conn = $this->_masterConnection;
} else {
$this->_conn = $this->_slaveConnection;
}
}

/**
* Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
* and returns the number of affected rows.
*
* This method supports PDO binding types as well as DBAL mapping types.
*
* @param string $query The SQL query.
* @param array $params The query parameters.
* @param array $types The parameter types.
* @return integer The number of affected rows.
* @internal PERF: Directly prepares a driver statement, not a wrapper.
*/
public function executeUpdate($query, array $params = array(), array $types = array())
{
$this->connect();
$this->_conn = $this->_masterConnection;
return parent::executeUpdate($query, $params, $types);
}

/**
* Execute an SQL statement and return the number of affected rows.
*
* @param string $statement
* @return integer The number of affected rows.
*/
public function exec($statement)
{
$this->connect();
$this->_switchConnectionByQuery($statement);

return parent::exec($statement);
}

/**
* Returns the ID of the last inserted row, or the last value from a sequence object,
* depending on the underlying driver.
*
* Note: This method may not return a meaningful or consistent result across different drivers,
* because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
* columns or sequences.
*
* @param string $seqName Name of the sequence object from which the ID should be returned.
* @return string A string representation of the last inserted ID.
*/
public function lastInsertId($seqName = null)
{
$this->connect();
$this->_conn = $this->_masterConnection;

return parent::lastInsertId($seqName);
}

/* OVERWRITTEN METHODS */
/**
* Gets the name of the database this Connection is connected to.
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getDatabase()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}

/**
* Gets the hostname of the currently connected database.
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getHost()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}

/**
* Gets the port of the currently connected database.
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getPort()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}

/**
* Gets the username used by this connection.
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getUsername()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}

/**
* Gets the password used by this connection.
*
* @throws \Doctrine\DBAL\DBALException
*/
public function getPassword()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}

/**
* Executes an SQL statement, returning a result set as a Statement object.
*
* @param string $statement
* @param integer $fetchType
* @return Doctrine\DBAL\Driver\Statement
*/
public function query()
{
throw new \Doctrine\DBAL\DBALException(‚Not implemented in this connection‘);
}
}

[/code]

Soweit ich bis jetzt testen konnte funktioniert das sogar. 😉
Freu mich auf Feedback!

GD Star Rating
loading...

Kommentar verfassen