PageRenderTime 50ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/koowa/database/query/query.php

https://bitbucket.org/organicdevelopment/nooku-framework
PHP | 485 lines | 229 code | 76 blank | 180 comment | 23 complexity | 8eeab56753e88713207dcebc182e7c8f MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * @version $Id$
  4. * @package Koowa_Database
  5. * @subpackage Query
  6. * @copyright Copyright (C) 2007 - 2012 Johan Janssens. All rights reserved.
  7. * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
  8. * @link http://www.nooku.org
  9. */
  10. /**
  11. * Database Select Class for database select statement generation
  12. *
  13. * @author Johan Janssens <johan@nooku.org>
  14. * @package Koowa_Database
  15. * @subpackage Query
  16. */
  17. class KDatabaseQuery
  18. {
  19. /**
  20. * Count operation
  21. *
  22. * @var boolean
  23. */
  24. public $count = false;
  25. /**
  26. * Distinct operation
  27. *
  28. * @var boolean
  29. */
  30. public $distinct = false;
  31. /**
  32. * The columns
  33. *
  34. * @var array
  35. */
  36. public $columns = array();
  37. /**
  38. * The from element
  39. *
  40. * @var array
  41. */
  42. public $from = array();
  43. /**
  44. * The join element
  45. *
  46. * @var array
  47. */
  48. public $join = array();
  49. /**
  50. * The where element
  51. *
  52. * @var array
  53. */
  54. public $where = array();
  55. /**
  56. * The group element
  57. *
  58. * @var array
  59. */
  60. public $group = array();
  61. /**
  62. * The having element
  63. *
  64. * @var array
  65. */
  66. public $having = array();
  67. /**
  68. * The order element
  69. *
  70. * @var string
  71. */
  72. public $order = array();
  73. /**
  74. * The limit element
  75. *
  76. * @var integer
  77. */
  78. public $limit = null;
  79. /**
  80. * The limit offset element
  81. *
  82. * @var integer
  83. */
  84. public $offset = null;
  85. /**
  86. * Database connector
  87. *
  88. * @var object
  89. */
  90. protected $_adapter;
  91. /**
  92. * Table prefix
  93. *
  94. * @var object
  95. */
  96. protected $_prefix;
  97. /**
  98. * Object constructor
  99. *
  100. * Can be overloaded/supplemented by the child class
  101. *
  102. * @param object An optional KConfig object with configuration options.
  103. */
  104. public function __construct( KConfig $config )
  105. {
  106. //If no config is passed create it
  107. if(!isset($config)) $config = new KConfig();
  108. //Initialise the object
  109. $this->_initialize($config);
  110. //set the model adapter
  111. $this->_adapter = $config->adapter;
  112. }
  113. /**
  114. * Initializes the options for the object
  115. *
  116. * @param object An optional KConfig object with configuration options.
  117. */
  118. protected function _initialize(KConfig $config)
  119. {
  120. $config->append(array(
  121. 'adapter' => '',
  122. ));
  123. }
  124. /**
  125. * Gets the database adapter for this particular KDatabaseQuery object.
  126. *
  127. * @return KDatabaseAdapterInterface
  128. */
  129. public function getAdapter()
  130. {
  131. return $this->_adapter;
  132. }
  133. /**
  134. * Set the database adapter for this particular KDatabaseQuery object.
  135. *
  136. * @param object A KDatabaseAdapterInterface object
  137. * @return KDatabaseQuery
  138. */
  139. public function setAdapter(KDatabaseAdapterInterface $adapter)
  140. {
  141. $this->_adapter = $adapter;
  142. return $this;
  143. }
  144. /**
  145. * Built a select query
  146. *
  147. * @param array|string A string or an array of column names
  148. * @return KDatabaseQuery
  149. */
  150. public function select( $columns = '*')
  151. {
  152. settype($columns, 'array');
  153. $this->columns = array_unique( array_merge( $this->columns, $columns ) );
  154. return $this;
  155. }
  156. /**
  157. * Built a count query
  158. *
  159. * @return KDatabaseQuery
  160. */
  161. public function count()
  162. {
  163. $this->count = true;
  164. $this->columns = array();
  165. return $this;
  166. }
  167. /**
  168. * Make the query distinct
  169. *
  170. * @return KDatabaseQuery
  171. */
  172. public function distinct()
  173. {
  174. $this->distinct = true;
  175. return $this;
  176. }
  177. /**
  178. * Built the from clause of the query
  179. *
  180. * @param array|string A string or array of table names
  181. * @return KDatabaseQuery
  182. */
  183. public function from( $tables )
  184. {
  185. settype($tables, 'array');
  186. //The table needle
  187. $needle = $this->_adapter->getTableNeedle();
  188. //Prepent the table prefix
  189. foreach($tables as &$table)
  190. {
  191. if(strpos($table, $needle) !== 0) {
  192. $table = $needle.$table;
  193. }
  194. }
  195. $this->from = array_unique(array_merge($this->from, $tables));
  196. return $this;
  197. }
  198. /**
  199. * Built the join clause of the query
  200. *
  201. * @param string The type of join; empty for a plain JOIN, or "LEFT", "INNER", etc.
  202. * @param string The table name to join to.
  203. * @param string|array Join on this condition.
  204. * @return KDatabaseQuery
  205. */
  206. public function join($type, $table, $condition)
  207. {
  208. settype($condition, 'array');
  209. //The table needle
  210. $needle = $this->_adapter->getTableNeedle();
  211. if(strpos($table, $needle) !== 0) {
  212. $table = $needle.$table;
  213. }
  214. $this->join[] = array(
  215. 'type' => strtoupper($type),
  216. 'table' => $table,
  217. 'condition' => $condition,
  218. );
  219. return $this;
  220. }
  221. /**
  222. * Built the where clause of the query
  223. *
  224. * @param string The name of the property the constraint applies too, or a SQL function or statement
  225. * @param string The comparison used for the constraint
  226. * @param string|array The value compared to the property value using the constraint
  227. * @param string The where condition, defaults to 'AND'
  228. * @return KDatabaseQuery
  229. */
  230. public function where( $property, $constraint = null, $value = null, $condition = 'AND' )
  231. {
  232. if(!empty($property))
  233. {
  234. $where = array();
  235. $where['property'] = $property;
  236. if(isset($constraint))
  237. {
  238. $constraint = strtoupper($constraint);
  239. $condition = strtoupper($condition);
  240. $where['constraint'] = $constraint;
  241. $where['value'] = $value;
  242. }
  243. $where['condition'] = count($this->where) ? $condition : '';
  244. //Make sure we don't store the same where clauses twice
  245. $property = is_array($property) ? implode(';',$property) : $property;
  246. $constraint = is_array($constraint) ? implode(';',$constraint) : $constraint;
  247. $value = is_array($value) ? implode(';',$value) : $value;
  248. $signature = md5( $property.$constraint.$value );
  249. if(!isset($this->where[$signature])) {
  250. $this->where[$signature] = $where;
  251. }
  252. }
  253. return $this;
  254. }
  255. /**
  256. * Built the group clause of the query
  257. *
  258. * @param array|string A string or array of ordering columns
  259. * @return KDatabaseQuery
  260. */
  261. public function group( $columns )
  262. {
  263. settype($columns, 'array'); //force to an array
  264. $this->group = array_unique( array_merge( $this->group, $columns));
  265. return $this;
  266. }
  267. /**
  268. * Built the having clause of the query
  269. *
  270. * @param array|string A string or array of ordering columns
  271. * @return KDatabaseQuery
  272. */
  273. public function having( $columns )
  274. {
  275. settype($columns, 'array'); //force to an array
  276. $this->having = array_unique( array_merge( $this->having, $columns ));
  277. return $this;
  278. }
  279. /**
  280. * Build the order clause of the query
  281. *
  282. * @param array|string A string or array of ordering columns
  283. * @param string Either DESC or ASC
  284. * @return KDatabaseQuery
  285. */
  286. public function order( $columns, $direction = 'ASC' )
  287. {
  288. settype($columns, 'array'); //force to an array
  289. foreach($columns as $column)
  290. {
  291. $this->order[] = array(
  292. 'column' => $column,
  293. 'direction' => $direction
  294. );
  295. }
  296. return $this;
  297. }
  298. /**
  299. * Built the limit element of the query
  300. *
  301. * @param integer Number of items to fetch.
  302. * @param integer Offset to start fetching at.
  303. * @return KDatabaseQuery
  304. */
  305. public function limit( $limit, $offset = 0 )
  306. {
  307. $this->limit = (int) $limit;
  308. $this->offset = (int) $offset;
  309. return $this;
  310. }
  311. /**
  312. * Render the query to a string
  313. *
  314. * @return string The completed query
  315. */
  316. public function __toString()
  317. {
  318. $query = '';
  319. if(!empty($this->columns) || $this->count)
  320. {
  321. $query = 'SELECT';
  322. if($this->distinct) {
  323. $query .= ' DISTINCT';
  324. }
  325. if($this->count) {
  326. $query .= ' COUNT(*)';
  327. }
  328. }
  329. if (!empty($this->columns) && ! $this->count)
  330. {
  331. $columns = array();
  332. foreach($this->columns as $column) {
  333. $columns[] = $this->_adapter->quoteName($column);
  334. }
  335. $query .= ' '.implode(' , ', $columns);
  336. }
  337. if (!empty($this->from))
  338. {
  339. $tables = array();
  340. foreach($this->from as $table) {
  341. $tables[] = $this->_adapter->quoteName($table);
  342. }
  343. $query .= ' FROM '.implode(' , ', $tables);
  344. }
  345. if (!empty($this->join))
  346. {
  347. $joins = array();
  348. foreach ($this->join as $join)
  349. {
  350. $tmp = ' ';
  351. if (! empty($join['type'])) {
  352. $tmp .= $join['type'] . ' ';
  353. }
  354. $tmp .= ' JOIN ' . $this->_adapter->quoteName($join['table']);
  355. $tmp .= ' ON (' . implode(' AND ', $this->_adapter->quoteName($join['condition'])) . ')';
  356. $joins[] = $tmp;
  357. }
  358. $query .= implode(' ', $joins);
  359. }
  360. if (!empty($this->where))
  361. {
  362. $query .= ' WHERE';
  363. foreach($this->where as $where)
  364. {
  365. if(isset($where['condition'])) {
  366. $query .= ' '.$where['condition'];
  367. }
  368. $query .= ' '. $this->_adapter->quoteName($where['property']);
  369. if(isset($where['constraint']))
  370. {
  371. $value = $this->_adapter->quoteValue($where['value']);
  372. if(in_array($where['constraint'], array('IN', 'NOT IN'))) {
  373. $value = ' ( '.$value. ' ) ';
  374. }
  375. $query .= ' '.$where['constraint'].' '.$value;
  376. }
  377. }
  378. }
  379. if (!empty($this->group))
  380. {
  381. $columns = array();
  382. foreach($this->group as $column) {
  383. $columns[] = $this->_adapter->quoteName($column);
  384. }
  385. $query .= ' GROUP BY '.implode(' , ', $columns);
  386. }
  387. if (!empty($this->having))
  388. {
  389. $columns = array();
  390. foreach($this->having as $column) {
  391. $columns[] = $this->_adapter->quoteName($column);
  392. }
  393. $query .= ' HAVING '.implode(' , ', $columns);
  394. }
  395. if (!empty($this->order) )
  396. {
  397. $query .= ' ORDER BY ';
  398. $list = array();
  399. foreach ($this->order as $order) {
  400. $list[] = $this->_adapter->quoteName($order['column']).' '.$order['direction'];
  401. }
  402. $query .= implode(' , ', $list);
  403. }
  404. if (!empty($this->limit)) {
  405. $query .= ' LIMIT '.$this->offset.' , '.$this->limit;
  406. }
  407. return $query;
  408. }
  409. }