grammar.php 9.65 KB
Newer Older
1
<?php namespace Laravel\Database\Query\Grammars;
2 3 4

use Laravel\Database\Query;
use Laravel\Database\Expression;
Taylor Otwell committed
5

6
class Grammar extends \Laravel\Database\Grammar {
Taylor Otwell committed
7 8

	/**
9
	 * All of the query componenets in the order they should be built.
Taylor Otwell committed
10
	 *
11
	 * @var array
Taylor Otwell committed
12
	 */
13
	protected $components = array(
14 15
		'aggregate', 'selects', 'from', 'joins', 'wheres',
		'groupings', 'orderings', 'limit', 'offset',
16
	);
Taylor Otwell committed
17 18 19 20

	/**
	 * Compile a SQL SELECT statement from a Query instance.
	 *
Taylor Otwell committed
21 22 23
	 * @param  Query   $query
	 * @return string
	 */
24
	public function select(Query $query)
Taylor Otwell committed
25
	{
26 27
		return $this->concatenate($this->components($query));
	}
Taylor Otwell committed
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
	/**
	 * Generate the SQL for every component of the query.
	 *
	 * @param  Query  $query
	 * @return array
	 */
	final protected function components($query)
	{
		// Each portion of the statement is compiled by a function corresponding
		// to an item in the components array. This lets us to keep the creation
		// of the query very granular, and allows for the flexible customization
		// of the query building process by each database system's grammar.
		//
		// Note that each component also corresponds to a public property on the
		// query instance, allowing us to pass the appropriate data into each of
		// the compiler functions.
Taylor Otwell committed
45
		foreach ($this->components as $component)
Taylor Otwell committed
46
		{
47 48
			if ( ! is_null($query->$component))
			{
49
				$sql[$component] = call_user_func(array($this, $component), $query);
50
			}
Taylor Otwell committed
51 52
		}

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
		return (array) $sql;
	}

	/**
	 * Concatenate an array of SQL segments, removing those that are empty.
	 *
	 * @param  array   $components
	 * @return string
	 */
	final protected function concatenate($components)
	{
		return implode(' ', array_filter($components, function($value)
		{
			return (string) $value !== '';
		}));
Taylor Otwell committed
68 69 70
	}

	/**
Taylor Otwell committed
71
	 * Compile the SELECT clause for a query.
Taylor Otwell committed
72 73 74 75
	 *
	 * @param  Query   $query
	 * @return string
	 */
Taylor Otwell committed
76
	protected function selects(Query $query)
Taylor Otwell committed
77
	{
78 79 80
		$select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';

		return $select.$this->columnize($query->selects);
Taylor Otwell committed
81 82 83
	}

	/**
Taylor Otwell committed
84
	 * Compile an aggregating SELECT clause for a query.
Taylor Otwell committed
85
	 *
Taylor Otwell committed
86
	 * @param  Query   $query
Taylor Otwell committed
87 88
	 * @return string
	 */
Taylor Otwell committed
89
	protected function aggregate(Query $query)
Taylor Otwell committed
90
	{
Taylor Otwell committed
91 92 93
		$column = $this->wrap($query->aggregate['column']);

		return 'SELECT '.$query->aggregate['aggregator'].'('.$column.')';
Taylor Otwell committed
94 95 96
	}

	/**
Taylor Otwell committed
97
	 * Compile the FROM clause for a query.
Taylor Otwell committed
98
	 *
Taylor Otwell committed
99
	 * @param  Query   $query
Taylor Otwell committed
100 101
	 * @return string
	 */
Taylor Otwell committed
102
	protected function from(Query $query)
Taylor Otwell committed
103
	{
Taylor Otwell committed
104
		return 'FROM '.$this->wrap($query->from);
Taylor Otwell committed
105 106 107
	}

	/**
Taylor Otwell committed
108
	 * Compile the JOIN clauses for a query.
Taylor Otwell committed
109
	 *
Taylor Otwell committed
110
	 * @param  Query   $query
Taylor Otwell committed
111 112
	 * @return string
	 */
Taylor Otwell committed
113
	protected function joins(Query $query)
Taylor Otwell committed
114
	{
115 116 117 118 119 120 121
		// We need to iterate through each JOIN clause that is attached to the
		// query an translate it into SQL. The table and the columns will be
		// wrapped in identifiers to avoid naming collisions.
		//
		// Once all of the JOINs have been compiled, we can concatenate them
		// together using a single space, which should give us the complete
		// set of joins in valid SQL that can appended to the query.
Taylor Otwell committed
122
		foreach ($query->joins as $join)
Taylor Otwell committed
123
		{
124
			$table = $this->wrap($join['table']);
Taylor Otwell committed
125

126
			$column1 = $this->wrap($join['column1']);
127

128
			$column2 = $this->wrap($join['column2']);
129

130
			$sql[] = "{$join['type']} JOIN {$table} ON {$column1} {$join['operator']} {$column2}";
Taylor Otwell committed
131 132 133 134 135 136
		}

		return implode(' ', $sql);
	}

	/**
Taylor Otwell committed
137
	 * Compile the WHERE clause for a query.
Taylor Otwell committed
138
	 *
Taylor Otwell committed
139
	 * @param  Query   $query
Taylor Otwell committed
140 141
	 * @return string
	 */
142
	final protected function wheres(Query $query)
Taylor Otwell committed
143
	{
144
		// Each WHERE clause array has a "type" that is assigned by the query
145 146 147 148 149 150
		// builder, and each type has its own compiler function. We will call
		// the appropriate compiler for each where clause in the query.
		//
		// Keeping each particular where clause in its own "compiler" allows
		// us to keep the query generation process very granular, making it
		// easier to customize derived grammars for other databases.
151
		foreach ($query->wheres as $where)
Taylor Otwell committed
152
		{
153
			$sql[] = $where['connector'].' '.$this->{$where['type']}($where);
Taylor Otwell committed
154 155
		}

156 157 158 159 160 161 162
		if  (isset($sql))
		{
			// We attach the boolean connector to every where segment just
			// for convenience. Once we have built the entire clause we'll
			// remove the first instance of a connector from the clause.
			return 'WHERE '.preg_replace('/AND |OR /', '', implode(' ', $sql), 1);
		}
Taylor Otwell committed
163 164 165
	}

	/**
166
	 * Compile a nested WHERE clause.
Taylor Otwell committed
167
	 *
168 169 170 171 172 173 174 175 176 177 178 179 180 181
	 * @param  array   $where
	 * @return string
	 */
	protected function where_nested($where)
	{
		// To generate a nested WHERE clause, we'll just feed the query
		// back into the "wheres" method. Once we have the clause, we
		// will strip off the first six characters to get rid of the
		// leading WHERE keyword.
		return '('.substr($this->wheres($where['query']), 6).')';
	}

	/**
	 * Compile a simple WHERE clause.
182
	 *
Taylor Otwell committed
183 184 185
	 * @param  array   $where
	 * @return string
	 */
Taylor Otwell committed
186
	protected function where($where)
Taylor Otwell committed
187
	{
188 189 190
		$parameter = $this->parameter($where['value']);

		return $this->wrap($where['column']).' '.$where['operator'].' '.$parameter;
Taylor Otwell committed
191 192 193 194 195 196 197 198
	}

	/**
	 * Compile a WHERE IN clause.
	 *
	 * @param  array   $where
	 * @return string
	 */
Taylor Otwell committed
199
	protected function where_in($where)
Taylor Otwell committed
200
	{
201 202 203
		$parameters = $this->parameterize($where['values']);

		return $this->wrap($where['column']).' IN ('.$parameters.')';
204
	}
Taylor Otwell committed
205

206 207 208 209 210 211 212 213
	/**
	 * Compile a WHERE NOT IN clause.
	 *
	 * @param  array   $where
	 * @return string
	 */
	protected function where_not_in($where)
	{
214 215 216
		$parameters = $this->parameterize($where['values']);

		return $this->wrap($where['column']).' NOT IN ('.$parameters.')';
Taylor Otwell committed
217 218 219 220 221 222 223 224
	}

	/**
	 * Compile a WHERE NULL clause.
	 *
	 * @param  array   $where
	 * @return string
	 */
Taylor Otwell committed
225
	protected function where_null($where)
Taylor Otwell committed
226
	{
227 228
		return $this->wrap($where['column']).' IS NULL';
	}
Taylor Otwell committed
229

230 231 232 233 234 235 236 237 238 239 240 241 242 243
	/**
	 * 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.
	 *
244
	 * @param  array   $where
245 246
	 * @return string
	 */
247
	final protected function where_raw($where)
248
	{
249
		return $where['sql'];
Taylor Otwell committed
250 251 252
	}

	/**
253 254 255 256 257 258 259 260 261 262 263
	 * Compile the GROUP BY clause for a query.
	 *
	 * @param  Query   $query
	 * @return string
	 */
	protected function groupings(Query $query)
	{
		return 'GROUP BY '.$this->columnize($query->groupings);
	}

	/**
264
	 * Compile the ORDER BY clause for a query.
Taylor Otwell committed
265
	 *
Taylor Otwell committed
266
	 * @param  Query   $query
Taylor Otwell committed
267 268
	 * @return string
	 */
Taylor Otwell committed
269
	protected function orderings(Query $query)
Taylor Otwell committed
270
	{
Taylor Otwell committed
271
		foreach ($query->orderings as $ordering)
Taylor Otwell committed
272
		{
273 274 275
			$direction = strtoupper($ordering['direction']);

			$sql[] = $this->wrap($ordering['column']).' '.$direction;
Taylor Otwell committed
276 277 278 279 280 281
		}

		return 'ORDER BY '.implode(', ', $sql);
	}

	/**
Taylor Otwell committed
282
	 * Compile the LIMIT clause for a query.
Taylor Otwell committed
283
	 *
Taylor Otwell committed
284
	 * @param  Query   $query
Taylor Otwell committed
285 286
	 * @return string
	 */
Taylor Otwell committed
287
	protected function limit(Query $query)
Taylor Otwell committed
288
	{
Taylor Otwell committed
289
		return 'LIMIT '.$query->limit;
Taylor Otwell committed
290 291 292
	}

	/**
Taylor Otwell committed
293
	 * Compile the OFFSET clause for a query.
Taylor Otwell committed
294
	 *
Taylor Otwell committed
295
	 * @param  Query   $query
Taylor Otwell committed
296 297
	 * @return string
	 */
Taylor Otwell committed
298
	protected function offset(Query $query)
Taylor Otwell committed
299
	{
Taylor Otwell committed
300
		return 'OFFSET '.$query->offset;
Taylor Otwell committed
301 302 303 304 305
	}

	/**
	 * Compile a SQL INSERT statment from a Query instance.
	 *
306
	 * This method handles the compilation of single row inserts and batch inserts.
307
	 *
Taylor Otwell committed
308 309 310 311 312 313
	 * @param  Query   $query
	 * @param  array   $values
	 * @return string
	 */
	public function insert(Query $query, $values)
	{
314 315 316
		$table = $this->wrap($query->from);

		// Force every insert to be treated like a batch insert. This simply makes
317 318
		// creating the SQL syntax a little easier on us since we can always treat
		// the values as if it is an array containing multiple inserts.
319 320
		if ( ! is_array(reset($values))) $values = array($values);

321
		// Since we only care about the column names, we can pass any of the insert
322 323
		// arrays into the "columnize" method. The columns should be the same for
		// every insert to the table so we can just use the first record.
324 325
		$columns = $this->columnize(array_keys(reset($values)));

326 327 328
		// Build the list of parameter place-holders of values bound to the query.
		// Each insert should have the same number of bound paramters, so we can
		// just use the first array of values.
329 330
		$parameters = $this->parameterize(reset($values));

331
		$parameters = implode(', ', array_fill(0, count($values), "($parameters)"));
Taylor Otwell committed
332

333
		return "INSERT INTO {$table} ({$columns}) VALUES {$parameters}";
Taylor Otwell committed
334 335 336
	}

	/**
Taylor Otwell committed
337
	 * Compile a SQL UPDATE statment from a Query instance.
Taylor Otwell committed
338 339 340 341 342
	 *
	 * @param  Query   $query
	 * @param  array   $values
	 * @return string
	 */
Taylor Otwell committed
343
	public function update(Query $query, $values)
Taylor Otwell committed
344
	{
345 346 347 348 349 350
		$table = $this->wrap($query->from);

		// Each column in the UPDATE statement needs to be wrapped in keyword
		// identifiers, and a place-holder needs to be created for each value
		// in the array of bindings. Of course, if the value of the binding
		// is an expression, the expression string will be injected.
351 352
		foreach ($values as $column => $value)
		{
353
			$columns[] = $this->wrap($column).' = '.$this->parameter($value);
354
		}
Taylor Otwell committed
355

356 357
		$columns = implode(', ', $columns);

358 359 360 361 362
		// UPDATE statements may be constrained by a WHERE clause, so we'll
		// run the entire where compilation process for those contraints.
		// This is easily achieved by passing the query to the "wheres"
		// method which will call all of the where compilers.
		return trim("UPDATE {$table} SET {$columns} ".$this->wheres($query));
Taylor Otwell committed
363 364 365
	}

	/**
Taylor Otwell committed
366
	 * Compile a SQL DELETE statment from a Query instance.
Taylor Otwell committed
367 368 369 370
	 *
	 * @param  Query   $query
	 * @return string
	 */
Taylor Otwell committed
371
	public function delete(Query $query)
Taylor Otwell committed
372
	{
373
		$table = $this->wrap($query->from);
Taylor Otwell committed
374

375 376 377 378 379
		// Like the UPDATE statement, the DELETE statement is constrained
		// by WHERE clauses, so we'll need to run the "wheres" method to
		// make the WHERE clauses for the query. The "wheres" method 
		// encapsulates the logic to create the full WHERE clause.
		return trim("DELETE FROM {$table} ".$this->wheres($query));
Taylor Otwell committed
380
	}
Taylor Otwell committed
381 382

}