diff --git a/laravel/bootstrap/errors.php b/laravel/bootstrap/errors.php index e8d1dc53..a9111ff6 100644 --- a/laravel/bootstrap/errors.php +++ b/laravel/bootstrap/errors.php @@ -49,11 +49,13 @@ */ set_exception_handler(function($e) use ($handler) { + //die(var_dump($e)); $handler($e); }); set_error_handler(function($number, $error, $file, $line) use ($handler) { + //die($error); $handler(new \ErrorException($error, $number, 0, $file, $line)); }); @@ -61,6 +63,7 @@ { if ( ! is_null($error = error_get_last())) { + //die($error['message']); $handler(new \ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); } }); diff --git a/laravel/database/connectors/postgres.php b/laravel/database/connectors/postgres.php index e13d7227..028ef8c6 100644 --- a/laravel/database/connectors/postgres.php +++ b/laravel/database/connectors/postgres.php @@ -36,7 +36,7 @@ protected function dsn($config) // 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) + foreach (array('port') as $key => $value) { if (isset($config[$key])) $dsn .= ";{$key}={$value}"; } diff --git a/laravel/database/grammars/grammar.php b/laravel/database/grammars/grammar.php index 823df1e7..47ad7a9c 100644 --- a/laravel/database/grammars/grammar.php +++ b/laravel/database/grammars/grammar.php @@ -2,13 +2,6 @@ class Grammar { - /** - * All of the query componenets in the order they should be built. - * - * @var array - */ - protected $components = array('selects', 'from', 'joins', 'wheres', 'orderings', 'limit', 'offset'); - /** * The keyword identifier for the database system. * @@ -16,6 +9,13 @@ class Grammar { */ protected $wrapper = '"'; + /** + * All of the query componenets in the order they should be built. + * + * @var array + */ + protected $components = array('aggregate', 'selects', 'from', 'joins', 'wheres', 'orderings', 'limit', 'offset'); + /** * Compile a SQL SELECT statement from a Query instance. * @@ -26,7 +26,7 @@ class Grammar { * @param Query $query * @return string */ - public function select(Query $query) + final public function select(Query $query) { $sql = array(); @@ -48,17 +48,12 @@ public function select(Query $query) */ protected function selects(Query $query) { - if ( ! is_null($query->aggregate)) return $this->aggregate($query); - return (($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ').$this->columnize($query->selects); } /** * Compile an aggregating SELECT clause for a query. * - * This method compiled the SELECT clauses for queries built using the - * count, max, min, abs, and sum methods on the fluent query builder. - * * @param Query $query * @return string */ @@ -88,11 +83,17 @@ protected function from(Query $query) */ protected function joins(Query $query) { + // Since creating a JOIN clause using string concatenation is a little cumbersome, + // we will create a format we can pass to "sprintf" to make things cleaner. + $format = '%s JOIN %s ON %s %s %s'; + foreach ($query->joins as $join) { extract($join); - $sql[] = $type.' JOIN '.$this->wrap($table).' ON '.$this->wrap($column1).' '.$operator.' '.$this->wrap($column2); + list($column1, $column2) = array($this->wrap($column1), $this->wrap($column2)); + + $sql[] = sprintf($format, $type, $this->wrap($table), $column1, $operator, $column2); } return implode(' ', $sql); @@ -104,7 +105,7 @@ protected function joins(Query $query) * @param Query $query * @return string */ - protected function wheres(Query $query) + final protected function wheres(Query $query) { // Each WHERE clause array has a "type" that is assigned by the query builder, and // each type has its own compiler function. For example, "where in" queries are @@ -117,14 +118,12 @@ protected function wheres(Query $query) $sql[] = ($where['type'] == 'raw') ? $where['sql'] : $where['connector'].' '.$this->{$where['type']}($where); } - return implode(' ', array_merge(array('WHERE 1 = 1'), $sql)); + if (isset($sql)) return implode(' ', array_merge(array('WHERE 1 = 1'), $sql)); } /** * Compile a simple WHERE clause. * - * This method compiles the SQL for the "where" and "or_where" query functions. - * * @param array $where * @return string */ @@ -136,8 +135,6 @@ protected function where($where) /** * Compile a WHERE IN clause. * - * This method compiled the SQL for all of the "where_in" style query functions. - * * @param array $where * @return string */ @@ -151,8 +148,6 @@ protected function where_in($where) /** * Compile a WHERE NULL clause. * - * This method compiles the SQL for all of the "where_null" style query functions. - * * @param array $where * @return string */ @@ -204,34 +199,48 @@ protected function offset(Query $query) /** * Compile a SQL INSERT statment from a Query instance. * + * Note: This method handles the compilation of single row inserts and batch inserts. + * * @param Query $query * @param array $values * @return string */ public function insert(Query $query, $values) { - $columns = implode(', ', $this->columnize(array_keys($values))); + // Force every insert to be treated like a batch insert. This simply makes creating + // the SQL syntax a little easier on us since we can always treat the values as if + // is an array containing multiple inserts. + if ( ! is_array(reset($values))) $values = array($values); - return 'INSERT INTO '.$this->wrap($query->from).' ('.$columns.') VALUES ('.$this->parameterize($values).')'; + // Since we only care about the column names, we can pass any of the insert arrays + // into the "columnize" method. The names should be the same for every insert. + $columns = $this->columnize(array_keys(reset($values))); + + // We need to create a string of comma-delimited insert segments. Each segment contains + // PDO place-holders for each value being inserted into the table. So, if we are inserting + // into three columns, the string should look like this: + // + // (?, ?, ?), (?, ?, ?), (?, ?, ?) + $parameters = implode(', ', array_fill(0, count($values), '('.$this->parameterize(reset($values)).')')); + + return 'INSERT INTO '.$this->wrap($query->from).' ('.$columns.') VALUES '.$parameters; } /** * Compile a SQL UPDATE statment from a Query instance. * + * Note: Since UPDATE statements can be limited by a WHERE clause, this method will + * use the same WHERE clause compilation functions as the "select" method. + * * @param Query $query * @param array $values * @return string */ public function update(Query $query, $values) { - foreach (array_keys($values) as $column) - { - $sets[] = $this->wrap($column).' = ?'; - } + $columns = $this->columnize(array_keys($values), ' = ?'); - $sql = 'UPDATE '.$this->wrap($query->from).' SET '.implode(', ', $sets); - - return (count($query->wheres) > 0) ? $sql.' '.$this->wheres($query->wheres) : $sql; + return trim('UPDATE '.$this->wrap($query->from).' SET '.$columns.' '.$this->wheres($query)); } /** @@ -242,35 +251,50 @@ public function update(Query $query, $values) */ public function delete(Query $query) { - $sql = 'DELETE FROM '.$this->wrap($query->from); - - return (count($query->wheres) > 0) ? $sql.' '.$this->wheres($query->wheres) : $sql; + return trim('DELETE FROM '.$this->wrap($query->from).' '.$this->wheres($query)); } + /** + * The following functions primarily serve as utility functions for the grammar. + * They perform tasks such as wrapping values in keyword identifiers or creating + * variable lists of bindings. Most likely, they will not need to change across + * various database systems. + */ + /** * Create a comma-delimited list of wrapped column names. * + * Optionally, an "append" value may be passed to the method. This value will be + * appended to every wrapped column name. + * * @param array $columns + * @param string $append * @return string */ - protected function columnize($columns) + protected function columnize($columns, $append = '') { - return implode(', ', array_map(array($this, 'wrap'), $columns)); + foreach ($columns as $column) { $sql[] = $this->wrap($column).$append; } + + return implode(', ', $sql); } /** * Wrap a value in keyword identifiers. * + * They keyword identifier used by the method is specified as a property on + * the grammar class so it can be conveniently overriden without changing + * the wrapping logic itself. + * * @param string $value * @return string */ - public function wrap($value) + protected function wrap($value) { - if (strpos(strtolower($value), ' as ') !== false) return $this->wrap_alias($value); + if (strpos(strtolower($value), ' as ') !== false) return $this->alias($value); foreach (explode('.', $value) as $segment) { - $wrapped[] = ($segment != '*') ? $this->wrapper.$segment.$this->wrapper : $segment; + $wrapped[] = ($segment !== '*') ? $this->wrapper.$segment.$this->wrapper : $segment; } return implode('.', $wrapped); @@ -282,7 +306,7 @@ public function wrap($value) * @param string $value * @return string */ - public function wrap_alias($value) + protected function alias($value) { $segments = explode(' ', $value); @@ -295,7 +319,7 @@ public function wrap_alias($value) * @param array $values * @return string */ - public function parameterize($values) + protected function parameterize($values) { return implode(', ', array_fill(0, count($values), '?')); } diff --git a/laravel/database/query.php b/laravel/database/query.php index 03e33576..b74d7f14 100644 --- a/laravel/database/query.php +++ b/laravel/database/query.php @@ -468,9 +468,9 @@ private function aggregate($aggregator, $column) $result = $this->connection->scalar($this->grammar->select($this), $this->bindings); - // Reset the SELECT clause so more queries can be performed using the same instance. + // Reset the aggregate so more queries can be performed using the same instance. // This is helpful for getting aggregates and then getting actual results. - $this->selects = null; + $this->aggregate = null; return $result; } @@ -526,7 +526,19 @@ public function get($columns = array('*')) */ public function insert($values) { - return $this->connection->query($this->grammar->insert($this, $values), array_values($values)); + // Force every insert to be treated like a batch insert. This simply makes creating + // the binding array easier. We will simply loop through each inserted row and merge + // the values together to get one big binding array. + if ( ! is_array(reset($values))) $values = array($values); + + $bindings = array(); + + foreach ($values as $value) + { + $bindings = array_merge($bindings, array_values($value)); + } + + return $this->connection->query($this->grammar->insert($this, $values), $bindings); } /**