From cb5a426cbafc00116d2c991c1fd5fea54f1744ff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 17 Sep 2011 23:46:24 -0500 Subject: [PATCH] added database connectors and cleaned up configuration. --- laravel/config/container.php | 2 +- laravel/database/connection.php | 20 +++++--- laravel/database/connectors/connector.php | 41 ++++++++++++++++ laravel/database/connectors/mysql.php | 47 ++++++++++++++++++ laravel/database/connectors/postgres.php | 47 ++++++++++++++++++ laravel/database/connectors/sqlite.php | 31 ++++++++++++ laravel/database/manager.php | 58 ++++++++++++++++++----- laravel/database/query.php | 21 ++++++-- 8 files changed, 244 insertions(+), 23 deletions(-) create mode 100644 laravel/database/connectors/connector.php create mode 100644 laravel/database/connectors/mysql.php create mode 100644 laravel/database/connectors/postgres.php create mode 100644 laravel/database/connectors/sqlite.php diff --git a/laravel/config/container.php b/laravel/config/container.php index 277a3b14..8d43ee0b 100644 --- a/laravel/config/container.php +++ b/laravel/config/container.php @@ -49,7 +49,7 @@ return array( 'laravel.database' => array('singleton' => true, 'resolver' => function($container) { - return new Database\Manager($container->resolve('laravel.config')->get('database')); + return new Database\Manager($container->resolve('laravel.config')); }), diff --git a/laravel/database/connection.php b/laravel/database/connection.php index bc7998c8..de9681bb 100644 --- a/laravel/database/connection.php +++ b/laravel/database/connection.php @@ -5,6 +5,13 @@ use PDOStatement; class Connection { + /** + * The connection configuration array. + * + * @var array + */ + protected $config; + /** * The PDO connection. * @@ -22,12 +29,14 @@ class Connection { /** * Create a new database connection instance. * - * @param PDO $pdo + * @param PDO $pdo + * @param array $config * @return void */ - public function __construct(PDO $pdo) + public function __construct(PDO $pdo, $config) { $this->pdo = $pdo; + $this->config = $config; } /** @@ -114,18 +123,15 @@ class Connection { /** * Create a new query grammar for the connection. * - * @return Queries\Grammars\Grammar + * @return Grammars\Grammar */ protected function grammar() { - switch ($this->driver()) + switch (isset($this->config['grammar']) ? $this->config['grammar'] : $this->driver()) { case 'mysql': return new Grammars\MySQL; - case 'pgsql': - return new Grammars\Postgres; - default: return new Grammars\Grammar; } diff --git a/laravel/database/connectors/connector.php b/laravel/database/connectors/connector.php new file mode 100644 index 00000000..caedbe78 --- /dev/null +++ b/laravel/database/connectors/connector.php @@ -0,0 +1,41 @@ + PDO::CASE_LOWER, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + PDO::ATTR_EMULATE_PREPARES => false, + ); + + /** + * Establish a PDO database connection for a given database configuration. + * + * @param array $config + * @return PDO + */ + abstract public function connect($config); + + /** + * Get the PDO connection options for a given database configuration. + * + * Developer specified options will override the default connection options. + * + * @param array $config + * @return array + */ + protected function options($config) + { + $options = (isset($config['options'])) ? $config['options'] : array(); + + return array_merge($this->options, $options); + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/mysql.php b/laravel/database/connectors/mysql.php new file mode 100644 index 00000000..2b401603 --- /dev/null +++ b/laravel/database/connectors/mysql.php @@ -0,0 +1,47 @@ +dsn($config), $config['username'], $config['password'], $this->options($config)); + + if (isset($config['charset'])) + { + $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); + } + + return $connection; + } + + /** + * Format the DSN connection string for a MySQL connection. + * + * @param array $config + * @return string + */ + protected function dsn($config) + { + // Format the initial MySQL PDO connection string. These options are required + // for every MySQL connection that is established. The connection strings + // have the following convention: "mysql:host=hostname;dbname=database" + $dsn = sprintf('%s:host=%s;dbname=%s', $config['driver'], $config['host'], $config['database']); + + // Check for any optional MySQL PDO options. These options are not required + // to establish a PDO connection; however, may be needed in certain server + // or hosting environments used by the developer. + foreach (array('port', 'unix_socket') as $key => $value) + { + if (isset($config[$key])) $dsn .= ";{$key}={$value}"; + } + + return $dsn; + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/postgres.php b/laravel/database/connectors/postgres.php new file mode 100644 index 00000000..e13d7227 --- /dev/null +++ b/laravel/database/connectors/postgres.php @@ -0,0 +1,47 @@ +dsn($config), $config['username'], $config['password'], $this->options($config)); + + if (isset($config['charset'])) + { + $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); + } + + return $connection; + } + + /** + * Format the DSN connection string for a Postgres connection. + * + * @param array $config + * @return string + */ + protected function dsn($config) + { + // Format the initial Postgres PDO connection string. These options are required + // for every Postgres connection that is established. The connection strings + // have the following convention: "pgsql:host=hostname;dbname=database" + $dsn = sprintf('%s:host=%s;dbname=%s', $config['driver'], $config['host'], $config['database']); + + // Check for any optional Postgres PDO options. These options are not required + // to establish a PDO connection; however, may be needed in certain server + // or hosting environments used by the developer. + foreach (array('port', 'unix_socket') as $key => $value) + { + if (isset($config[$key])) $dsn .= ";{$key}={$value}"; + } + + return $dsn; + } + +} \ No newline at end of file diff --git a/laravel/database/connectors/sqlite.php b/laravel/database/connectors/sqlite.php new file mode 100644 index 00000000..bfcab4ad --- /dev/null +++ b/laravel/database/connectors/sqlite.php @@ -0,0 +1,31 @@ +options($config); + + if ($config['database'] == ':memory:') + { + return new PDO('sqlite::memory:', null, null, $options); + } + elseif (file_exists($path = DATABASE_PATH.$config['database'].'.sqlite')) + { + return new PDO('sqlite:'.$path, null, null, $options); + } + elseif (file_exists($config['database'])) + { + return new PDO('sqlite:'.$config['database'], null, null, $options); + } + + throw new \Exception("SQLite database [{$config['database']}] could not be found."); + } + +} \ No newline at end of file diff --git a/laravel/database/manager.php b/laravel/database/manager.php index 3d9c9fea..b7cb6844 100644 --- a/laravel/database/manager.php +++ b/laravel/database/manager.php @@ -1,5 +1,7 @@ config = $config; } @@ -32,27 +41,54 @@ class Manager { */ public function connection($connection = null) { - if (is_null($connection)) $connection = $this->config['default']; + if (is_null($connection)) $connection = $this->config->get('database.default'); if ( ! array_key_exists($connection, $this->connections)) { - if ( ! isset($this->config['connectors'][$connection])) + $config = $this->config->get("database.connections.{$connection}"); + + if (is_null($config)) { throw new \Exception("Database connection configuration is not defined for connection [$connection]."); } - // Database connections are established by developer configurable connector closures. - // This provides the developer the maximum amount of freedom in establishing their - // database connections, and allows the framework to remain agonstic to ugly database - // specific PDO connection details. Less code. Less bugs. - $pdo = call_user_func($this->config['connectors'][$connection], $this->config); - - $this->connections[$connection] = new Connection($pdo, $this->config); + $this->connections[$connection] = new Connection($this->connect($config), $config); } return $this->connections[$connection]; } + /** + * Get a PDO database connection for a given database configuration. + * + * @param array $config + * @return PDO + */ + protected function connect($config) + { + if (isset($config['connector'])) { return call_user_func($config['connector'], $config); } + + switch ($config['driver']) + { + case 'sqlite': + $connector = new Connectors\SQLite; + break; + + case 'mysql': + $connector = new Connectors\MySQL; + break; + + case 'pgsql': + $connector = new Connectors\Postgres; + break; + + default: + throw new \Exception("Database driver [{$config['driver']}] is not supported."); + } + + return $connector->connect($config); + } + /** * Begin a fluent query against a table. * diff --git a/laravel/database/query.php b/laravel/database/query.php index 6b850e4e..43e8f390 100644 --- a/laravel/database/query.php +++ b/laravel/database/query.php @@ -122,7 +122,7 @@ class Query { */ public function select($columns = array('*')) { - $this->select = $columns; + $this->select = (array) $columns; return $this; } @@ -478,12 +478,25 @@ class Query { /** * Execute the query as a SELECT statement and return the first result. * - * @param array $columns - * @return stdClass + * If a single column is selected from the database, only the value of that column will be returned. + * + * @param array $columns + * @return mixed */ public function first($columns = array('*')) { - return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null; + $columns = (array) $columns; + + $results = (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null; + + // If we have results and only a single column was selected from the database, + // we will simply return the value of that column for convenience. + if ( ! is_null($results) and count($columns) == 1 and $columns[0] !== '*') + { + return $results->{$columns[0]}; + } + + return $results; } /**