Some applications may use multiple database servers; for example, one for writes, and one or more for reads. The ConnectionLocator allows you to define multiple Connection objects for lazy-loaded read and write connections. It will create the connections only when they are when called. The Connection creation logic should be wrapped in factory callable.
The easiest way to create a ConnectionLocator is to use its static new()
method, either with PDO connection arguments, or with an actual PDO instance:
use Atlas\Pdo\ConnectionLocator;
// pass PDO constructor arguments ...
$connectionLocator = ConnectionLocator::new(
'mysql:host=localhost;dbname=testdb',
'username',
'password'
);
// ... or a PDO instance.
$connectionLocator = ConnectionLocator::new($pdo);
Doing so will define the default connection factory for the ConnectionLocator.
Once you have a ConnectionLocator, you can add as many named read and write connection factories as you like:
// the write (master) server
$connectionLocator->setWriteFactory('master', Connection::factory(
'mysql:host=master.db.localhost;dbname=database',
'username',
'password'
));
// read (slave) #1
$connectionLocator->setReadFactory('slave1', Connection::factory(
'mysql:host=slave1.db.localhost;dbname=database',
'username',
'password'
));
// read (slave) #2
$connectionLocator->setReadFactory('slave2', Connection::factory(
'mysql:host=slave2.db.localhost;dbname=database',
'username',
'password'
));
// read (slave) #3
$connectionLocator->setReadFactory('slave3', Connection::factory(
'mysql:host=slave3.db.localhost;dbname=database',
'username',
'password'
));
Retrieve a Connection from the locator when you need it. This will create the Connection (if needed) and then return it.
getDefault()
will return the default Connection.
getRead()
will return a random read Connection; after the first call, getRead()
will always return the same Connection. (If no read Connections are defined, it will return the default connection.)
getWrite()
will return a random write Connection; after the first call, getWrite()
will always return the same Connection. (If no write Connections are defined, it will return the default connection.)
$read = $connectionLocator->getRead();
$results = $read->fetchAll('SELECT * FROM table_name LIMIT 10');
$readAgain = $connectionLocator->getRead();
assert($read === $readAgain); // true
You can get any read or write connection directly by name using the get()
method:
$foo = $connectionLocator->get(ConnectionLocator::READ, 'foo');
$bar = $connectionLocator->get(ConnectionLocator::WRITE, 'bar');
If you call the lockToWrite()
method, calls to getRead()
will return the write connection instead of the read connection.
$read = $connectionLocator->getRead();
$write = $connectionLocator->getWrite();
$connectionLocator->lockToWrite();
$readAgain = $connectionLocator->getRead();
assert($readAgain === $write); // true
You can disable the lock-to-write behavior by calling lockToWrite(false)
.
The ConnectionLocator can be configured with all its connections at construction time; this can be useful with dependency injection mechanisms. (Note that this requires using the constructor proper, not the static new()
method.)
use Atlas\Pdo\Connection;
use Atlas\Pdo\ConnectionLocator;
// default connection
$default = Connection::factory(
'mysql:host=default.db.localhost;dbname=database',
'username',
'password'
);
// read connections
$read = [
'slave1' => Connection::factory(
'mysql:host=slave1.db.localhost;dbname=database',
'username',
'password'
),
'slave2' => Connection::factory(
'mysql:host=slave2.db.localhost;dbname=database',
'username',
'password'
),
'slave3' => Connection::factory(
'mysql:host=slave3.db.localhost;dbname=database',
'username',
'password'
),
];
// write connection
$write = [
'master' => Connection::factory(
'mysql:host=master.db.localhost;dbname=database',
'username',
'password'
),
];
// configure locator at construction time
$connectionLocator = new ConnectionLocator($default, $read, $write);
As with an individual Connection, it is sometimes useful to log all
queries on all connections in the ConnectionLocator. To do so, call its
logQueries()
method, issue your queries, and then call getQueries()
to
get back the log entries.
// start logging
$connectionLocator->logQueries(true);
// retrieve connections and issue queries, then:
$queries = $connectionLocator->getQueries();
// stop logging
$connectionLocator->logQueries(false);
Each query log entry will have one added key, connection
, indicating which
connection performed the query. The connection
label will be DEFAULT
for the
default connection, READ:
and the read connection name, or WRITE:
and the
write connection name.
Note:
Calling
logQueries()
will turn logging on and off for all instances in the locator, even if those instances are not "in hand" at the moment. That is, you do not have to re-get the instance; logging for each connection will be turned on and off "at a distance."
You may wish to set a custom logger on the ConnectionLocator. To do so, call
setQueryLogger()
and pass a callable with the signature
function (array $entry) : void
.
class CustomDebugger
{
public function __invoke(array $entry) : void
{
// call an injected logger to record the entry
}
}
$customDebugger = new CustomDebugger();
$connectionLocator->setQueryLogger($customDebugger);
$connectionLocator->logQueries(true);
// now the Connection will send query log entries to the CustomDebugger
Note:
If you set a custom logger, the Connection will no longer retain its own query log entries; they will all go to the custom logger. This means that
getQueries()
on the Connection not show any new entries.