grammar.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <?php namespace Laravel\Database\Query\Grammars;
  2. use Laravel\Database\Query;
  3. use Laravel\Database\Expression;
  4. class Grammar extends \Laravel\Database\Grammar {
  5. /**
  6. * The format for properly saving a DateTime.
  7. *
  8. * @var string
  9. */
  10. public $datetime = 'Y-m-d H:i:s';
  11. /**
  12. * All of the query components in the order they should be built.
  13. *
  14. * @var array
  15. */
  16. protected $components = array(
  17. 'aggregate', 'selects', 'from', 'joins', 'wheres',
  18. 'groupings', 'havings', 'orderings', 'limit', 'offset',
  19. );
  20. /**
  21. * Compile a SQL SELECT statement from a Query instance.
  22. *
  23. * @param Query $query
  24. * @return string
  25. */
  26. public function select(Query $query)
  27. {
  28. return $this->concatenate($this->components($query));
  29. }
  30. /**
  31. * Generate the SQL for every component of the query.
  32. *
  33. * @param Query $query
  34. * @return array
  35. */
  36. final protected function components($query)
  37. {
  38. // Each portion of the statement is compiled by a function corresponding
  39. // to an item in the components array. This lets us to keep the creation
  40. // of the query very granular and very flexible.
  41. foreach ($this->components as $component)
  42. {
  43. if ( ! is_null($query->$component))
  44. {
  45. $sql[$component] = call_user_func(array($this, $component), $query);
  46. }
  47. }
  48. return (array) $sql;
  49. }
  50. /**
  51. * Concatenate an array of SQL segments, removing those that are empty.
  52. *
  53. * @param array $components
  54. * @return string
  55. */
  56. final protected function concatenate($components)
  57. {
  58. return implode(' ', array_filter($components, function($value)
  59. {
  60. return (string) $value !== '';
  61. }));
  62. }
  63. /**
  64. * Compile the SELECT clause for a query.
  65. *
  66. * @param Query $query
  67. * @return string
  68. */
  69. protected function selects(Query $query)
  70. {
  71. if ( ! is_null($query->aggregate)) return;
  72. $select = ($query->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';
  73. return $select.$this->columnize($query->selects);
  74. }
  75. /**
  76. * Compile an aggregating SELECT clause for a query.
  77. *
  78. * @param Query $query
  79. * @return string
  80. */
  81. protected function aggregate(Query $query)
  82. {
  83. $column = $this->columnize($query->aggregate['columns']);
  84. // If the "distinct" flag is set and we're not aggregating everything
  85. // we'll set the distinct clause on the query, since this is used
  86. // to count all of the distinct values in a column, etc.
  87. if ($query->distinct and $column !== '*')
  88. {
  89. $column = 'DISTINCT '.$column;
  90. }
  91. return 'SELECT '.$query->aggregate['aggregator'].'('.$column.') AS '.$this->wrap('aggregate');
  92. }
  93. /**
  94. * Compile the FROM clause for a query.
  95. *
  96. * @param Query $query
  97. * @return string
  98. */
  99. protected function from(Query $query)
  100. {
  101. return 'FROM '.$this->wrap_table($query->from);
  102. }
  103. /**
  104. * Compile the JOIN clauses for a query.
  105. *
  106. * @param Query $query
  107. * @return string
  108. */
  109. protected function joins(Query $query)
  110. {
  111. // We need to iterate through each JOIN clause that is attached to the
  112. // query and translate it into SQL. The table and the columns will be
  113. // wrapped in identifiers to avoid naming collisions.
  114. foreach ($query->joins as $join)
  115. {
  116. $table = $this->wrap_table($join->table);
  117. $clauses = array();
  118. // Each JOIN statement may have multiple clauses, so we will iterate
  119. // through each clause creating the conditions then we'll join all
  120. // of them together at the end to build the clause.
  121. foreach ($join->clauses as $clause)
  122. {
  123. extract($clause);
  124. $column1 = $this->wrap($column1);
  125. $column2 = $this->wrap($column2);
  126. $clauses[] = "{$connector} {$column1} {$operator} {$column2}";
  127. }
  128. // The first clause will have a connector on the front, but it is
  129. // not needed on the first condition, so we will strip it off of
  130. // the condition before adding it to the array of joins.
  131. $search = array('AND ', 'OR ');
  132. $clauses[0] = str_replace($search, '', $clauses[0]);
  133. $clauses = implode(' ', $clauses);
  134. $sql[] = "{$join->type} JOIN {$table} ON {$clauses}";
  135. }
  136. // Finally, we should have an array of JOIN clauses that we can
  137. // implode together and return as the complete SQL for the
  138. // join clause of the query under construction.
  139. return implode(' ', $sql);
  140. }
  141. /**
  142. * Compile the WHERE clause for a query.
  143. *
  144. * @param Query $query
  145. * @return string
  146. */
  147. final protected function wheres(Query $query)
  148. {
  149. if (is_null($query->wheres)) return '';
  150. // Each WHERE clause array has a "type" that is assigned by the query
  151. // builder, and each type has its own compiler function. We will call
  152. // the appropriate compiler for each where clause.
  153. foreach ($query->wheres as $where)
  154. {
  155. $sql[] = $where['connector'].' '.$this->{$where['type']}($where);
  156. }
  157. if (isset($sql))
  158. {
  159. // We attach the boolean connector to every where segment just
  160. // for convenience. Once we have built the entire clause we'll
  161. // remove the first instance of a connector.
  162. return 'WHERE '.preg_replace('/AND |OR /', '', implode(' ', $sql), 1);
  163. }
  164. }
  165. /**
  166. * Compile a nested WHERE clause.
  167. *
  168. * @param array $where
  169. * @return string
  170. */
  171. protected function where_nested($where)
  172. {
  173. return '('.substr($this->wheres($where['query']), 6).')';
  174. }
  175. /**
  176. * Compile a simple WHERE clause.
  177. *
  178. * @param array $where
  179. * @return string
  180. */
  181. protected function where($where)
  182. {
  183. $parameter = $this->parameter($where['value']);
  184. return $this->wrap($where['column']).' '.$where['operator'].' '.$parameter;
  185. }
  186. /**
  187. * Compile a WHERE IN clause.
  188. *
  189. * @param array $where
  190. * @return string
  191. */
  192. protected function where_in($where)
  193. {
  194. $parameters = $this->parameterize($where['values']);
  195. return $this->wrap($where['column']).' IN ('.$parameters.')';
  196. }
  197. /**
  198. * Compile a WHERE NOT IN clause.
  199. *
  200. * @param array $where
  201. * @return string
  202. */
  203. protected function where_not_in($where)
  204. {
  205. $parameters = $this->parameterize($where['values']);
  206. return $this->wrap($where['column']).' NOT IN ('.$parameters.')';
  207. }
  208. /**
  209. * Compile a WHERE NULL clause.
  210. *
  211. * @param array $where
  212. * @return string
  213. */
  214. protected function where_null($where)
  215. {
  216. return $this->wrap($where['column']).' IS NULL';
  217. }
  218. /**
  219. * Compile a WHERE NULL clause.
  220. *
  221. * @param array $where
  222. * @return string
  223. */
  224. protected function where_not_null($where)
  225. {
  226. return $this->wrap($where['column']).' IS NOT NULL';
  227. }
  228. /**
  229. * Compile a raw WHERE clause.
  230. *
  231. * @param array $where
  232. * @return string
  233. */
  234. final protected function where_raw($where)
  235. {
  236. return $where['sql'];
  237. }
  238. /**
  239. * Compile the GROUP BY clause for a query.
  240. *
  241. * @param Query $query
  242. * @return string
  243. */
  244. protected function groupings(Query $query)
  245. {
  246. return 'GROUP BY '.$this->columnize($query->groupings);
  247. }
  248. /**
  249. * Compile the HAVING clause for a query.
  250. *
  251. * @param Query $query
  252. * @return string
  253. */
  254. protected function havings(Query $query)
  255. {
  256. if (is_null($query->havings)) return '';
  257. foreach ($query->havings as $having)
  258. {
  259. $sql[] = 'AND '.$this->wrap($having['column']).' '.$having['operator'].' '.$this->parameter($having['value']);
  260. }
  261. return 'HAVING '.preg_replace('/AND /', '', implode(' ', $sql), 1);
  262. }
  263. /**
  264. * Compile the ORDER BY clause for a query.
  265. *
  266. * @param Query $query
  267. * @return string
  268. */
  269. protected function orderings(Query $query)
  270. {
  271. foreach ($query->orderings as $ordering)
  272. {
  273. $sql[] = $this->wrap($ordering['column']).' '.strtoupper($ordering['direction']);
  274. }
  275. return 'ORDER BY '.implode(', ', $sql);
  276. }
  277. /**
  278. * Compile the LIMIT clause for a query.
  279. *
  280. * @param Query $query
  281. * @return string
  282. */
  283. protected function limit(Query $query)
  284. {
  285. return 'LIMIT '.$query->limit;
  286. }
  287. /**
  288. * Compile the OFFSET clause for a query.
  289. *
  290. * @param Query $query
  291. * @return string
  292. */
  293. protected function offset(Query $query)
  294. {
  295. return 'OFFSET '.$query->offset;
  296. }
  297. /**
  298. * Compile a SQL INSERT statement from a Query instance.
  299. *
  300. * This method handles the compilation of single row inserts and batch inserts.
  301. *
  302. * @param Query $query
  303. * @param array $values
  304. * @return string
  305. */
  306. public function insert(Query $query, $values)
  307. {
  308. $table = $this->wrap_table($query->from);
  309. // Force every insert to be treated like a batch insert. This simply makes
  310. // creating the SQL syntax a little easier on us since we can always treat
  311. // the values as if it contains multiple inserts.
  312. if ( ! is_array(reset($values))) $values = array($values);
  313. // Since we only care about the column names, we can pass any of the insert
  314. // arrays into the "columnize" method. The columns should be the same for
  315. // every record inserted into the table.
  316. $columns = $this->columnize(array_keys(reset($values)));
  317. // Build the list of parameter place-holders of values bound to the query.
  318. // Each insert should have the same number of bound parameters, so we can
  319. // just use the first array of values.
  320. $parameters = $this->parameterize(reset($values));
  321. $parameters = implode(', ', array_fill(0, count($values), "($parameters)"));
  322. return "INSERT INTO {$table} ({$columns}) VALUES {$parameters}";
  323. }
  324. /**
  325. * Compile a SQL INSERT and get ID statement from a Query instance.
  326. *
  327. * @param Query $query
  328. * @param array $values
  329. * @param string $column
  330. * @return string
  331. */
  332. public function insert_get_id(Query $query, $values, $column)
  333. {
  334. return $this->insert($query, $values);
  335. }
  336. /**
  337. * Compile a SQL UPDATE statement from a Query instance.
  338. *
  339. * @param Query $query
  340. * @param array $values
  341. * @return string
  342. */
  343. public function update(Query $query, $values)
  344. {
  345. $table = $this->wrap_table($query->from);
  346. // Each column in the UPDATE statement needs to be wrapped in the keyword
  347. // identifiers, and a place-holder needs to be created for each value in
  348. // the array of bindings, so we'll build the sets first.
  349. foreach ($values as $column => $value)
  350. {
  351. $columns[] = $this->wrap($column).' = '.$this->parameter($value);
  352. }
  353. $columns = implode(', ', $columns);
  354. // UPDATE statements may be constrained by a WHERE clause, so we'll run
  355. // the entire where compilation process for those constraints. This is
  356. // easily achieved by passing it to the "wheres" method.
  357. return trim("UPDATE {$table} SET {$columns} ".$this->wheres($query));
  358. }
  359. /**
  360. * Compile a SQL DELETE statement from a Query instance.
  361. *
  362. * @param Query $query
  363. * @return string
  364. */
  365. public function delete(Query $query)
  366. {
  367. $table = $this->wrap_table($query->from);
  368. return trim("DELETE FROM {$table} ".$this->wheres($query));
  369. }
  370. /**
  371. * Transform an SQL short-cuts into real SQL for PDO.
  372. *
  373. * @param string $sql
  374. * @param array $bindings
  375. * @return string
  376. */
  377. public function shortcut($sql, &$bindings)
  378. {
  379. // Laravel provides an easy short-cut notation for writing raw WHERE IN
  380. // statements. If (...) is in the query, it will be replaced with the
  381. // correct number of parameters based on the query bindings.
  382. if (strpos($sql, '(...)') !== false)
  383. {
  384. for ($i = 0; $i < count($bindings); $i++)
  385. {
  386. // If the binding is an array, we can just assume it's used to fill a
  387. // where in condition, so we'll just replace the next place-holder
  388. // in the query with the constraint and splice the bindings.
  389. if (is_array($bindings[$i]))
  390. {
  391. $parameters = $this->parameterize($bindings[$i]);
  392. array_splice($bindings, $i, 1, $bindings[$i]);
  393. $sql = preg_replace('~\(\.\.\.\)~', "({$parameters})", $sql, 1);
  394. }
  395. }
  396. }
  397. return trim($sql);
  398. }
  399. }