ConnectionFactory.php
TLDR
This file, ConnectionFactory.php
, is a class that is responsible for creating database connections based on the provided configuration. It includes methods for creating single connections and read/write connections, as well as methods for parsing and merging configuration options. The file also includes a method for creating a PDO resolver and a method for creating a connector instance based on the configuration.
Methods
make
This method takes an array of configuration options and an optional connection name and returns a new Illuminate\Database\Connection
instance based on the configuration.
parseConfig
This method takes an array of configuration options and a connection name and returns a new array with the configured prefix and name added to the original configuration.
createSingleConnection
This method takes an array of configuration options and returns a new Illuminate\Database\Connection
instance for a single database connection.
createReadWriteConnection
This method takes an array of configuration options and returns a new Illuminate\Database\Connection
instance for a read/write database connection.
createReadPdo
This method takes an array of configuration options and returns a new PDO instance for reading.
getReadConfig
This method takes an array of configuration options and returns the read configuration for a read/write connection.
getWriteConfig
This method takes an array of configuration options and returns the write configuration for a read/write connection.
getReadWriteConfig
This method takes an array of configuration options and a type (read or write) and returns the corresponding configuration.
mergeReadWriteConfig
This method takes an array of configuration options and an array to merge and returns a new combined configuration for a read/write connection.
createPdoResolver
This method takes an array of configuration options and returns a closure that resolves to a PDO instance.
createPdoResolverWithHosts
This method takes an array of configuration options and returns a closure that resolves to a PDO instance with a specific host or an array of hosts.
parseHosts
This method takes an array of configuration options and returns an array of hosts.
createPdoResolverWithoutHosts
This method takes an array of configuration options and returns a closure that resolves to a PDO instance when there is no configured host.
createConnector
This method takes an array of configuration options and returns a Illuminate\Database\Connectors\ConnectorInterface
instance based on the configuration.
createConnection
This method takes a driver, a database connection, a database name, a prefix, and an array of configuration options and returns a new Illuminate\Database\Connection
instance.
<?php
namespace Illuminate\Database\Connectors;
use Illuminate\Contracts\Container\Container;
use Illuminate\Database\Connection;
use Illuminate\Database\MySqlConnection;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Database\SqlServerConnection;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use PDOException;
class ConnectionFactory
{
/**
* The IoC container instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $container;
/**
* Create a new connection factory instance.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return void
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* Establish a PDO connection based on the configuration.
*
* @param array $config
* @param string|null $name
* @return \Illuminate\Database\Connection
*/
public function make(array $config, $name = null)
{
$config = $this->parseConfig($config, $name);
if (isset($config['read'])) {
return $this->createReadWriteConnection($config);
}
return $this->createSingleConnection($config);
}
/**
* Parse and prepare the database configuration.
*
* @param array $config
* @param string $name
* @return array
*/
protected function parseConfig(array $config, $name)
{
return Arr::add(Arr::add($config, 'prefix', ''), 'name', $name);
}
/**
* Create a single database connection instance.
*
* @param array $config
* @return \Illuminate\Database\Connection
*/
protected function createSingleConnection(array $config)
{
$pdo = $this->createPdoResolver($config);
return $this->createConnection(
$config['driver'], $pdo, $config['database'], $config['prefix'], $config
);
}
/**
* Create a read / write database connection instance.
*
* @param array $config
* @return \Illuminate\Database\Connection
*/
protected function createReadWriteConnection(array $config)
{
$connection = $this->createSingleConnection($this->getWriteConfig($config));
return $connection->setReadPdo($this->createReadPdo($config));
}
/**
* Create a new PDO instance for reading.
*
* @param array $config
* @return \Closure
*/
protected function createReadPdo(array $config)
{
return $this->createPdoResolver($this->getReadConfig($config));
}
/**
* Get the read configuration for a read / write connection.
*
* @param array $config
* @return array
*/
protected function getReadConfig(array $config)
{
return $this->mergeReadWriteConfig(
$config, $this->getReadWriteConfig($config, 'read')
);
}
/**
* Get the write configuration for a read / write connection.
*
* @param array $config
* @return array
*/
protected function getWriteConfig(array $config)
{
return $this->mergeReadWriteConfig(
$config, $this->getReadWriteConfig($config, 'write')
);
}
/**
* Get a read / write level configuration.
*
* @param array $config
* @param string $type
* @return array
*/
protected function getReadWriteConfig(array $config, $type)
{
return isset($config[$type][0])
? Arr::random($config[$type])
: $config[$type];
}
/**
* Merge a configuration for a read / write connection.
*
* @param array $config
* @param array $merge
* @return array
*/
protected function mergeReadWriteConfig(array $config, array $merge)
{
return Arr::except(array_merge($config, $merge), ['read', 'write']);
}
/**
* Create a new Closure that resolves to a PDO instance.
*
* @param array $config
* @return \Closure
*/
protected function createPdoResolver(array $config)
{
return array_key_exists('host', $config)
? $this->createPdoResolverWithHosts($config)
: $this->createPdoResolverWithoutHosts($config);
}
/**
* Create a new Closure that resolves to a PDO instance with a specific host or an array of hosts.
*
* @param array $config
* @return \Closure
*
* @throws \PDOException
*/
protected function createPdoResolverWithHosts(array $config)
{
return function () use ($config) {
foreach (Arr::shuffle($this->parseHosts($config)) as $host) {
$config['host'] = $host;
try {
return $this->createConnector($config)->connect($config);
} catch (PDOException $e) {
continue;
}
}
throw $e;
};
}
/**
* Parse the hosts configuration item into an array.
*
* @param array $config
* @return array
*
* @throws \InvalidArgumentException
*/
protected function parseHosts(array $config)
{
$hosts = Arr::wrap($config['host']);
if (empty($hosts)) {
throw new InvalidArgumentException('Database hosts array is empty.');
}
return $hosts;
}
/**
* Create a new Closure that resolves to a PDO instance where there is no configured host.
*
* @param array $config
* @return \Closure
*/
protected function createPdoResolverWithoutHosts(array $config)
{
return fn () => $this->createConnector($config)->connect($config);
}
/**
* Create a connector instance based on the configuration.
*
* @param array $config
* @return \Illuminate\Database\Connectors\ConnectorInterface
*
* @throws \InvalidArgumentException
*/
public function createConnector(array $config)
{
if (! isset($config['driver'])) {
throw new InvalidArgumentException('A driver must be specified.');
}
if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
return $this->container->make($key);
}
return match ($config['driver']) {
'mysql' => new MySqlConnector,
'pgsql' => new PostgresConnector,
'sqlite' => new SQLiteConnector,
'sqlsrv' => new SqlServerConnector,
default => throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]."),
};
}
/**
* Create a new connection instance.
*
* @param string $driver
* @param \PDO|\Closure $connection
* @param string $database
* @param string $prefix
* @param array $config
* @return \Illuminate\Database\Connection
*
* @throws \InvalidArgumentException
*/
protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
{
if ($resolver = Connection::getResolver($driver)) {
return $resolver($connection, $database, $prefix, $config);
}
return match ($driver) {
'mysql' => new MySqlConnection($connection, $database, $prefix, $config),
'pgsql' => new PostgresConnection($connection, $database, $prefix, $config),
'sqlite' => new SQLiteConnection($connection, $database, $prefix, $config),
'sqlsrv' => new SqlServerConnection($connection, $database, $prefix, $config),
default => throw new InvalidArgumentException("Unsupported driver [{$driver}]."),
};
}
}