You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
8.9 KiB

11 years ago
  1. <?php namespace Majestic\Model;
  2. class SqlCriteria
  3. {
  4. const JOIN_TYPE_DEFAULT = 100;
  5. const JOIN_TYPE_LEFT = 101;
  6. const JOIN_TYPE_RIGHT = 102;
  7. const JOIN_TYPE_INNER = 103;
  8. const JOIN_TYPE_OUTER = 104;
  9. private static $join_reserved_keyword = array(
  10. self::JOIN_TYPE_DEFAULT => 'JOIN',
  11. self::JOIN_TYPE_LEFT => 'LEFT JOIN',
  12. self::JOIN_TYPE_RIGHT => 'RIGHT JOIN',
  13. self::JOIN_TYPE_INNER => 'INNER JOIN',
  14. self::JOIN_TYPE_OUTER => 'OUTER JOIN',
  15. );
  16. private $select = array();
  17. private $distinct = '';
  18. private $where = array();
  19. private $group_by = array();
  20. private $order = array('sort' => array(), 'order' => array());
  21. private $limit = '';
  22. /**
  23. * @var SqlModel
  24. */
  25. private $model;
  26. private $sql_expression;
  27. private $sql_expression_params = array();
  28. private $sql_join_expressions = array();
  29. private $join_table_placeholder = array();
  30. /**
  31. * @param $model SqlModel
  32. * @param $sql_expression string|null Sql expression with SELECT and FROM operators. If fetched, then SqlCriteria::select(), SqlCriteria::distinct() disabled for use.
  33. * @param $sql_expression_params array additional params to be replaced in sql expression
  34. */
  35. public function __construct($model, $sql_expression = null, $sql_expression_params = array())
  36. {
  37. $this->model = $model;
  38. $this->sql_expression = $sql_expression;
  39. $this->sql_expression_params = $sql_expression_params;
  40. }
  41. /**
  42. * @return SqlResultProvider
  43. */
  44. public function find()
  45. {
  46. $this->defineJoinExpressions();
  47. return $this->model->find($this->select, $this->distinct, $this->where, $this->order, $this->limit, null, $this->group_by, $this->sql_expression, $this->sql_expression_params);
  48. }
  49. private function defineJoinExpressions($select = null)
  50. {
  51. if ($this->sql_join_expressions) {
  52. if (!$this->sql_expression) {
  53. $select = $this->model->getDb()->selectExpr($select ? : $this->select, $this->distinct);
  54. $this->sql_expression = 'SELECT ' . $select . ' FROM :table';
  55. }
  56. $this->sql_expression .= ' ' . implode(' ', $this->sql_join_expressions);
  57. $this->sql_join_expressions = null;
  58. }
  59. }
  60. public function delete()
  61. {
  62. return $this->model->find('', '', $this->where, null, null, null, null, 'DELETE FROM :table', $this->sql_expression_params)->affectedRows();
  63. }
  64. //TODO: перенести определение sql_expression в модель
  65. public function count($select = null)
  66. {
  67. $this->defineJoinExpressions($select);
  68. $sql_expression_backup = $this->sql_expression;
  69. $group_by_backup = $this->group_by;
  70. $select_parent_query = 'COUNT(*) as count';
  71. $default_select = '*';
  72. if ($this->group_by) {
  73. $default_select = 'DISTINCT ' . $this->group_by[0];
  74. }
  75. $this->group_by = [];
  76. if (!$this->sql_expression) {
  77. $this->sql_expression = 'SELECT COUNT(' . ($select ? $select : $default_select) . ') as count FROM :table';
  78. $select_parent_query = 'x.count';
  79. } else {
  80. $this->sql_expression = preg_replace('#SELECT.+FROM#i', 'SELECT COUNT(' . ($select ? $select : $default_select) . ') as count FROM', $this->sql_expression);
  81. }
  82. $count = $this->model->count($select_parent_query, $this->where, null, $this->group_by, $this->sql_expression, $this->sql_expression_params)->fetchField('count');
  83. $this->sql_expression = $sql_expression_backup;
  84. $this->group_by = $group_by_backup;
  85. return $count;
  86. }
  87. private function defineJoinTablePlaceholder($table_name)
  88. {
  89. if (!isset($this->join_table_placeholder[$table_name])) {
  90. $this->join_table_placeholder[$table_name] = ':table' . (count($this->join_table_placeholder) + 1);
  91. }
  92. }
  93. public function getTablePh($table_name)
  94. {
  95. $this->defineJoinTablePlaceholder($table_name);
  96. return $this->join_table_placeholder[$table_name];
  97. }
  98. public function join($join_table_name, $join_field_name, $donor_table_name = null, $donor_field_name = null, $join_type = self::JOIN_TYPE_DEFAULT)
  99. {
  100. $donor_field_name = $donor_field_name ? : $join_field_name;
  101. $donor_table_placeholder = $donor_table_name ? $this->getTablePh($donor_table_name) : ':table';
  102. $join_table_placeholder = $this->getTablePh($join_table_name);
  103. $this->sql_join_expressions[] = self::$join_reserved_keyword[$join_type]
  104. . ' ' . $join_table_placeholder
  105. . ' ON ' . $donor_table_placeholder . '.' . $donor_field_name . '='
  106. . ' ' . $join_table_placeholder . '.' . $join_field_name;
  107. if ($donor_table_name) {
  108. $this->sql_expression_params[substr($donor_table_placeholder, 1)] = new DbExpr($this->model->identify($donor_table_name));
  109. }
  110. $this->sql_expression_params[substr($join_table_placeholder, 1)] = new DbExpr($this->model->identify($join_table_name));
  111. return $this;
  112. }
  113. /**
  114. * @param $cond string|array Condition with "?" placeholder @ex 'field=?' or 'field=1' or array('field=?' => 1', 'field=1')
  115. * @param $value string|array|DbExpr|null Value. Array transformed to DbExpr(implode(',' Array)) All elements in the array mast be integer
  116. * @return SqlCriteria
  117. * @desc Allow multiple calls
  118. */
  119. public function where($cond, $value = null)
  120. {
  121. if (is_null($value)) {
  122. if (is_array($cond)) {
  123. $this->where = $this->where + $cond;
  124. } else {
  125. $this->where[] = $cond;
  126. }
  127. } else {
  128. $this->where[$cond] = $value;
  129. }
  130. return $this;
  131. }
  132. public function whereJoin($join_table_name, $cond, $value = null)
  133. {
  134. $join_table_placeholder = $this->getTablePh($join_table_name);
  135. if (is_array($cond)) {
  136. $cond_replace = array();
  137. foreach ($cond as $key => $value) {
  138. $cond_replace[$this->getCondWithTablePlaceholderIfNeed($join_table_placeholder, $key)] = $value;
  139. }
  140. } else {
  141. $cond = $this->getCondWithTablePlaceholderIfNeed($join_table_placeholder, $cond);
  142. }
  143. return $this->where($cond, $value);
  144. }
  145. private function getCondWithTablePlaceholderIfNeed($table_placeholder, $cond)
  146. {
  147. if (!strstr('.', $cond)) {
  148. $cond = $table_placeholder . '.' . $cond;
  149. }
  150. return $cond;
  151. }
  152. /**
  153. * @param $field string
  154. * @param $value array
  155. * @return SqlCriteria
  156. */
  157. public function whereIn($field, $value)
  158. {
  159. return $this->where($field . ' in ?', $value);
  160. }
  161. /**
  162. * @param $field string
  163. * @param $value array
  164. * @return SqlCriteria
  165. */
  166. public function whereNotIn($field, $value)
  167. {
  168. return $this->where($field . ' not in ?', $value);
  169. }
  170. /**
  171. * @param $field string
  172. * @param $value array
  173. * @return SqlCriteria
  174. * @deprecated
  175. */
  176. public function whereNot($field, $value)
  177. {
  178. return $this->whereNotIn($field, $value);
  179. }
  180. public function groupBy($fields)
  181. {
  182. if (is_array($fields)) {
  183. $this->group_by = $this->group_by + $fields;
  184. } else {
  185. $this->group_by[] = $fields;
  186. }
  187. return $this;
  188. }
  189. /**
  190. * @param $field string Field @ex 'field'
  191. * @param $order_desc bool Descendant sort direction
  192. * @return SqlCriteria
  193. * @desc Allow multiple calls
  194. */
  195. public function order($field, $order_desc = false)
  196. {
  197. $this->order['sort'][] = $field;
  198. if ($order_desc) {
  199. $this->order['order'][$field] = 'desc';
  200. }
  201. return $this;
  202. }
  203. /**
  204. * @param $offset int
  205. * @param $limit int
  206. * @return SqlCriteria
  207. */
  208. public function limit($offset = 0, $limit)
  209. {
  210. if ($offset) {
  211. $this->limit = (int) $offset . ',';
  212. }
  213. $this->limit .= (int) $limit;
  214. return $this;
  215. }
  216. /**
  217. * @param string|array $fields
  218. * @param bool $convert_to_db_expression
  219. * @ex SqlCriteria::select('field')
  220. * @ex SqlCriteria->select(array('field1', 'field2'))
  221. * @ex SqlCriteria->select('field1,field2')
  222. * @return SqlCriteria
  223. */
  224. public function select($fields, $convert_to_db_expression = false)
  225. {
  226. if (!is_array($fields)) {
  227. $fields = explode(',', $fields);
  228. }
  229. $fields = array_map(function($item){return trim($item);},$fields);
  230. if ($convert_to_db_expression) {
  231. $fields = array_map(function($item){return new DbExpr($item);},$fields);
  232. }
  233. $this->select = array_merge($this->select,$fields);
  234. return $this;
  235. }
  236. /**
  237. * @param $field string|bool If true then distinct by *
  238. * @return SqlCriteria
  239. */
  240. public function distinct($field)
  241. {
  242. $this->distinct = $field;
  243. return $this;
  244. }
  245. }