From 8846110e8d7b0b1730f08a320317f6261b66b5ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Oct 2011 22:45:19 -0500 Subject: [PATCH] refactored and prettified the default database grammar. --- laravel/database/grammars/grammar.php | 102 +++++++++++++++++++------- laravel/database/query.php | 14 +++- 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/laravel/database/grammars/grammar.php b/laravel/database/grammars/grammar.php index acb1e1df..12099fbe 100644 --- a/laravel/database/grammars/grammar.php +++ b/laravel/database/grammars/grammar.php @@ -12,6 +12,10 @@ class Grammar { /** * All of the query componenets in the order they should be built. * + * Each derived compiler may adjust these components and place them in the + * order needed for its particular database system, providing greater + * control over how the query is structured. + * * @var array */ protected $components = array('aggregate', 'selects', 'from', 'joins', 'wheres', 'orderings', 'limit', 'offset'); @@ -19,9 +23,9 @@ class Grammar { /** * Compile a SQL SELECT statement from a Query instance. * - * The query will be compiled according to the order of the elements - * specified in the "components" property. The entire query is passed - * into each component compiler for convenience. + * The query will be compiled according to the order of the elements specified + * in the "components" property. The entire query is passed into each component + * compiler for convenience. * * @param Query $query * @return string @@ -30,9 +34,8 @@ final public function select(Query $query) { $sql = array(); - // Iterate through each query component, calling the compiler - // for that component, and passing the query instance into - // the compiler. + // Iterate through each query component, calling the compiler for that + // component and passing the query instance into the compiler. foreach ($this->components as $component) { if ( ! is_null($query->$component)) @@ -52,12 +55,19 @@ final public function select(Query $query) */ protected function selects(Query $query) { - return (($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ').$this->columnize($query->selects); + $select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; + + return $select.$this->columnize($query->selects); } /** * Compile an aggregating SELECT clause for a query. * + * If an aggregate function is called on the query instance, no select + * columns will be set, so it is safe to assume that the "selects" + * compiler function will not be called. We can simply build the + * aggregating select clause within this function. + * * @param Query $query * @return string */ @@ -69,6 +79,9 @@ protected function aggregate(Query $query) /** * Compile the FROM clause for a query. * + * This method should not handle the construction of "join" clauses. + * The join clauses will be constructured by their own compiler. + * * @param Query $query * @return string */ @@ -85,20 +98,24 @@ 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. + // 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, EXTR_SKIP); + // To save some space, we'll go ahead and wrap all of the elements + // that should wrapped in keyword identifiers. Each join clause will + // be added to an array of clauses and will be imploded after all + // of the clauses have been processed and compiled. + $table = $this->wrap($join['table']); - $column1 = $this->wrap($column1); + $column1 = $this->wrap($join['column1']); - $column2 = $this->wrap($column2); + $column2 = $this->wrap($join['column2']); - $sql[] = sprintf($format, $type, $this->wrap($table), $column1, $operator, $column2); + $sql[] = sprintf($format, $join['type'], $table, $column1, $join['operator'], $column2); } return implode(' ', $sql); @@ -112,16 +129,13 @@ protected function joins(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. - // The only exception to this rule are "raw" where clauses, - // which are simply appended to the query as-is, without - // any further compiling. + // Each WHERE clause array has a "type" that is assigned by the query + // builder, and each type has its own compiler function. We will simply + // iterate through the where clauses and call the appropriate compiler + // for each clause. foreach ($query->wheres as $where) { - $sql[] = ($where['type'] !== 'raw') - ? $where['connector'].' '.$this->{$where['type']}($where) - : $where['sql']; + $sql[] = $where['connector'].' '.$this->{$where['type']}($where); } if (isset($sql)) return implode(' ', array_merge(array('WHERE 1 = 1'), $sql)); @@ -130,6 +144,9 @@ final protected function wheres(Query $query) /** * Compile a simple WHERE clause. * + * This method handles the compilation of the structures created by the + * "where" and "or_where" methods on the query builder. + * * @param array $where * @return string */ @@ -146,9 +163,18 @@ protected function where($where) */ protected function where_in($where) { - $operator = ($where['not']) ? 'NOT IN' : 'IN'; + return $this->wrap($where['column']).' IN ('.$this->parameterize($where['values']).')'; + } - return $this->wrap($where['column']).' '.$operator.' ('.$this->parameterize($where['values']).')'; + /** + * Compile a WHERE NOT IN clause. + * + * @param array $where + * @return string + */ + protected function where_not_in($where) + { + return $this->wrap($where['column']).' NOT IN ('.$this->parameterize($where['values']).')'; } /** @@ -159,13 +185,33 @@ protected function where_in($where) */ protected function where_null($where) { - $operator = ($where['not']) ? 'NOT NULL' : 'NULL'; - - return $this->wrap($where['column']).' IS '.$operator; + return $this->wrap($where['column']).' IS NULL'; } /** - * Compile ORDER BY clause for a query. + * Compile a WHERE NULL clause. + * + * @param array $where + * @return string + */ + protected function where_not_null($where) + { + return $this->wrap($where['column']).' IS NOT NULL'; + } + + /** + * Compile a raw WHERE clause. + * + * @param string $where + * @return string + */ + protected function where_raw($where) + { + return $where; + } + + /** + * Compile the ORDER BY clause for a query. * * @param Query $query * @return string diff --git a/laravel/database/query.php b/laravel/database/query.php index 25ba2d79..9115ded2 100644 --- a/laravel/database/query.php +++ b/laravel/database/query.php @@ -250,7 +250,12 @@ public function or_where_id($value) */ public function where_in($column, $values, $connector = 'AND', $not = false) { - $this->wheres[] = array_merge(array('type' => 'where_in'), compact('column', 'values', 'connector', 'not')); + // The type set in this method will be used by the query grammar to call the + // appropriate compiler function for the where clause. For cleanliness, the + // compiler for "not in" and "in" statements is broken into two functions. + $type = ($not) ? 'where_not_in' : 'where_in'; + + $this->wheres[] = compact('type', 'column', 'values', 'connector'); $this->bindings = array_merge($this->bindings, $values); @@ -304,7 +309,12 @@ public function or_where_not_in($column, $values) */ public function where_null($column, $connector = 'AND', $not = false) { - $this->wheres[] = array_merge(array('type' => 'where_null'), compact('column', 'connector', 'not')); + // The type set in this method will be used by the query grammar to call the + // appropriate compiler function for the where clause. For cleanliness, the + // compiler for "not null" and "null" statements is broken into two functions. + $type = ($not) ? 'where_not_null' : 'where_null'; + + $this->wheres[] = compact('type', 'column', 'connector'); return $this; }