Select.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. /**
  3. * Database query builder for SELECT statements. See [Query Builder](/database/query/builder) for usage and examples.
  4. *
  5. * @package Kohana/Database
  6. * @category Query
  7. * @author Kohana Team
  8. * @copyright (c) Kohana Team
  9. * @license https://koseven.ga/LICENSE.md
  10. */
  11. class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where {
  12. // SELECT ...
  13. protected $_select = [];
  14. // DISTINCT
  15. protected $_distinct = FALSE;
  16. // FROM ...
  17. protected $_from = [];
  18. // JOIN ...
  19. protected $_join = [];
  20. // GROUP BY ...
  21. protected $_group_by = [];
  22. // HAVING ...
  23. protected $_having = [];
  24. // OFFSET ...
  25. protected $_offset = NULL;
  26. // UNION ...
  27. protected $_union = [];
  28. // The last JOIN statement created
  29. protected $_last_join;
  30. /**
  31. * Sets the initial columns to select from.
  32. *
  33. * @param array $columns column list
  34. * @return void
  35. */
  36. public function __construct(array $columns = NULL)
  37. {
  38. if ( ! empty($columns))
  39. {
  40. // Set the initial columns
  41. $this->_select = $columns;
  42. }
  43. // Start the query with no actual SQL statement
  44. parent::__construct(Database::SELECT, '');
  45. }
  46. /**
  47. * Enables or disables selecting only unique columns using "SELECT DISTINCT"
  48. *
  49. * @param boolean $value enable or disable distinct columns
  50. * @return $this
  51. */
  52. public function distinct($value)
  53. {
  54. $this->_distinct = (bool) $value;
  55. return $this;
  56. }
  57. /**
  58. * Choose the columns to select from.
  59. *
  60. * @param mixed $columns column name or array($column, $alias) or object
  61. * @return $this
  62. */
  63. public function select($columns = NULL)
  64. {
  65. $columns = func_get_args();
  66. $this->_select = array_merge($this->_select, $columns);
  67. return $this;
  68. }
  69. /**
  70. * Choose the columns to select from, using an array.
  71. *
  72. * @param array $columns list of column names or aliases
  73. * @return $this
  74. */
  75. public function select_array(array $columns)
  76. {
  77. $this->_select = array_merge($this->_select, $columns);
  78. return $this;
  79. }
  80. /**
  81. * Choose the tables to select "FROM ..."
  82. *
  83. * @param mixed $table table name or array($table, $alias) or object
  84. * @return $this
  85. */
  86. public function from($tables)
  87. {
  88. $tables = func_get_args();
  89. $this->_from = array_merge($this->_from, $tables);
  90. return $this;
  91. }
  92. /**
  93. * Adds addition tables to "JOIN ...".
  94. *
  95. * @param mixed $table column name or array($column, $alias) or object
  96. * @param string $type join type (LEFT, RIGHT, INNER, etc)
  97. * @return $this
  98. */
  99. public function join($table, $type = NULL)
  100. {
  101. $this->_join[] = $this->_last_join = new Database_Query_Builder_Join($table, $type);
  102. return $this;
  103. }
  104. /**
  105. * Adds "ON ..." conditions for the last created JOIN statement.
  106. *
  107. * @param mixed $c1 column name or array($column, $alias) or object
  108. * @param string $op logic operator
  109. * @param mixed $c2 column name or array($column, $alias) or object
  110. * @return $this
  111. */
  112. public function on($c1, $op, $c2)
  113. {
  114. $this->_last_join->on($c1, $op, $c2);
  115. return $this;
  116. }
  117. /**
  118. * Adds "USING ..." conditions for the last created JOIN statement.
  119. *
  120. * @param string $columns column name
  121. * @return $this
  122. */
  123. public function using($columns)
  124. {
  125. $columns = func_get_args();
  126. call_user_func_array([$this->_last_join, 'using'], $columns);
  127. return $this;
  128. }
  129. /**
  130. * Creates a "GROUP BY ..." filter.
  131. *
  132. * @param mixed $columns column name or array($column, $alias) or object
  133. * @return $this
  134. */
  135. public function group_by($columns)
  136. {
  137. $columns = func_get_args();
  138. $this->_group_by = array_merge($this->_group_by, $columns);
  139. return $this;
  140. }
  141. /**
  142. * Alias of and_having()
  143. *
  144. * @param mixed $column column name or array($column, $alias) or object
  145. * @param string $op logic operator
  146. * @param mixed $value column value
  147. * @return $this
  148. */
  149. public function having($column, $op, $value = NULL)
  150. {
  151. return $this->and_having($column, $op, $value);
  152. }
  153. /**
  154. * Creates a new "AND HAVING" condition for the query.
  155. *
  156. * @param mixed $column column name or array($column, $alias) or object
  157. * @param string $op logic operator
  158. * @param mixed $value column value
  159. * @return $this
  160. */
  161. public function and_having($column, $op, $value = NULL)
  162. {
  163. $this->_having[] = ['AND' => [$column, $op, $value]];
  164. return $this;
  165. }
  166. /**
  167. * Creates a new "OR HAVING" condition for the query.
  168. *
  169. * @param mixed $column column name or array($column, $alias) or object
  170. * @param string $op logic operator
  171. * @param mixed $value column value
  172. * @return $this
  173. */
  174. public function or_having($column, $op, $value = NULL)
  175. {
  176. $this->_having[] = ['OR' => [$column, $op, $value]];
  177. return $this;
  178. }
  179. /**
  180. * Alias of and_having_open()
  181. *
  182. * @return $this
  183. */
  184. public function having_open()
  185. {
  186. return $this->and_having_open();
  187. }
  188. /**
  189. * Opens a new "AND HAVING (...)" grouping.
  190. *
  191. * @return $this
  192. */
  193. public function and_having_open()
  194. {
  195. $this->_having[] = ['AND' => '('];
  196. return $this;
  197. }
  198. /**
  199. * Opens a new "OR HAVING (...)" grouping.
  200. *
  201. * @return $this
  202. */
  203. public function or_having_open()
  204. {
  205. $this->_having[] = ['OR' => '('];
  206. return $this;
  207. }
  208. /**
  209. * Closes an open "AND HAVING (...)" grouping.
  210. *
  211. * @return $this
  212. */
  213. public function having_close()
  214. {
  215. return $this->and_having_close();
  216. }
  217. /**
  218. * Closes an open "AND HAVING (...)" grouping.
  219. *
  220. * @return $this
  221. */
  222. public function and_having_close()
  223. {
  224. $this->_having[] = ['AND' => ')'];
  225. return $this;
  226. }
  227. /**
  228. * Closes an open "OR HAVING (...)" grouping.
  229. *
  230. * @return $this
  231. */
  232. public function or_having_close()
  233. {
  234. $this->_having[] = ['OR' => ')'];
  235. return $this;
  236. }
  237. /**
  238. * Adds an other UNION clause.
  239. *
  240. * @param mixed $select if string, it must be the name of a table. Else
  241. * must be an instance of Database_Query_Builder_Select
  242. * @param boolean $all decides if it's an UNION or UNION ALL clause
  243. * @return $this
  244. */
  245. public function union($select, $all = TRUE)
  246. {
  247. if (is_string($select))
  248. {
  249. $select = DB::select()->from($select);
  250. }
  251. if ( ! $select instanceof Database_Query_Builder_Select)
  252. throw new Kohana_Exception('first parameter must be a string or an instance of Database_Query_Builder_Select');
  253. $this->_union []= ['select' => $select, 'all' => $all];
  254. return $this;
  255. }
  256. /**
  257. * Start returning results after "OFFSET ..."
  258. *
  259. * @param integer $number starting result number or NULL to reset
  260. * @return $this
  261. */
  262. public function offset($number)
  263. {
  264. $this->_offset = ($number === NULL) ? NULL : (int) $number;
  265. return $this;
  266. }
  267. /**
  268. * Compile the SQL query and return it.
  269. *
  270. * @param mixed $db Database instance or name of instance
  271. * @return string
  272. */
  273. public function compile($db = NULL)
  274. {
  275. if ( ! is_object($db))
  276. {
  277. // Get the database instance
  278. $db = Database::instance($db);
  279. }
  280. // Callback to quote columns
  281. $quote_column = [$db, 'quote_column'];
  282. // Callback to quote tables
  283. $quote_table = [$db, 'quote_table'];
  284. // Start a selection query
  285. $query = 'SELECT ';
  286. if ($this->_distinct === TRUE)
  287. {
  288. // Select only unique results
  289. $query .= 'DISTINCT ';
  290. }
  291. if (empty($this->_select))
  292. {
  293. // Select all columns
  294. $query .= '*';
  295. }
  296. else
  297. {
  298. // Select all columns
  299. $query .= implode(', ', array_unique(array_map($quote_column, $this->_select)));
  300. }
  301. if ( ! empty($this->_from))
  302. {
  303. // Set tables to select from
  304. $query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from)));
  305. }
  306. if ( ! empty($this->_join))
  307. {
  308. // Add tables to join
  309. $query .= ' '.$this->_compile_join($db, $this->_join);
  310. }
  311. if ( ! empty($this->_where))
  312. {
  313. // Add selection conditions
  314. $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
  315. }
  316. if ( ! empty($this->_group_by))
  317. {
  318. // Add grouping
  319. $query .= ' '.$this->_compile_group_by($db, $this->_group_by);
  320. }
  321. if ( ! empty($this->_having))
  322. {
  323. // Add filtering conditions
  324. $query .= ' HAVING '.$this->_compile_conditions($db, $this->_having);
  325. }
  326. if ( ! empty($this->_order_by))
  327. {
  328. // Add sorting
  329. $query .= ' '.$this->_compile_order_by($db, $this->_order_by);
  330. }
  331. if ($this->_limit !== NULL)
  332. {
  333. // Add limiting
  334. $query .= ' LIMIT '.$this->_limit;
  335. }
  336. if ($this->_offset !== NULL)
  337. {
  338. // Add offsets
  339. $query .= ' OFFSET '.$this->_offset;
  340. }
  341. if ( ! empty($this->_union))
  342. {
  343. $query = '('.$query.')';
  344. foreach ($this->_union as $u) {
  345. $query .= ' UNION ';
  346. if ($u['all'] === TRUE)
  347. {
  348. $query .= 'ALL ';
  349. }
  350. $query .= '('.$u['select']->compile($db).')';
  351. }
  352. }
  353. $this->_sql = $query;
  354. return parent::compile($db);
  355. }
  356. public function reset()
  357. {
  358. $this->_select =
  359. $this->_from =
  360. $this->_join =
  361. $this->_where =
  362. $this->_group_by =
  363. $this->_having =
  364. $this->_order_by =
  365. $this->_union = [];
  366. $this->_distinct = FALSE;
  367. $this->_limit =
  368. $this->_offset =
  369. $this->_last_join = NULL;
  370. $this->_parameters = [];
  371. $this->_sql = NULL;
  372. return $this;
  373. }
  374. } // End Database_Query_Select