AclQueryFeatures.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. Lib::loadClass('ACL');
  3. Lib::loadClass('SqlQueryWhereBuilder');
  4. // usage: (Acl class)::buildQuery($params): return new AclQueryFeatures($this, $params);
  5. // (view): $queryFeatures = $acl->buildQuery($params);
  6. // (view): $total = $queryFeatures->getTotal();
  7. // (view): $items = $queryFeatures->getItems();
  8. // example: @see TableAcl, TableAjax
  9. class AclQueryFeatures {
  10. public $_params;
  11. public $_acl;
  12. public $_query;
  13. public $_total;
  14. public $_legacyMode;
  15. public function __construct($acl, $params) {
  16. $this->_acl = $acl;
  17. $this->_params = $params;
  18. $this->_query = null;
  19. $this->_total = null;
  20. $this->_legacyMode = false;
  21. // TODO: _legacyMode = ($from instanceof simple schema or another programmed objects)
  22. }
  23. public function parseQueryValue($fieldName, $searchQuery, $fieldType = 'xsd:string') {
  24. if ('!NULL' === $searchQuery) return ['is not null', null];
  25. if ('IS NOT NULL' === $searchQuery) return ['is not null', null];
  26. if ('NULL' === $searchQuery) return ['is null', null];
  27. if ('IS NULL' === $searchQuery) return ['is null', null];
  28. switch ($fieldType) {
  29. case 'gml:PolygonPropertyType':
  30. case 'gml:PointPropertyType':
  31. case 'gml:LineStringPropertyType':
  32. case 'gml:GeometryPropertyType': return $this->_parseGeomQuery($searchQuery);
  33. // $sqlFilter = $this->_sqlValueForGeomField($fldName, $v, 't');
  34. // if ('_CSV_NUM' == substr($fldName, -8)) { // if ($this->isCsvNumericField($fldName)) { // TODO: xsd type - p5:csv_num
  35. // $sqlFilter = $this->_sqlValueForCsvNumericField($fldName, $v, 't');
  36. // if ($sqlFilter) $sql_where_and[] = $sqlFilter;
  37. // continue;
  38. // }
  39. }
  40. switch (substr($searchQuery, 0, 1)) {
  41. case '=': return ['=', substr($searchQuery, 1)];
  42. case '>':
  43. switch (substr($searchQuery, 1, 1)) {
  44. case '=': return ['>=', substr($searchQuery, 2)];
  45. default: return ['>', substr($searchQuery, 1)];
  46. }
  47. case '<':
  48. switch (substr($searchQuery, 1, 1)) {
  49. case '=': return ['<=', substr($searchQuery, 2)];
  50. case '>': return ['!=', substr($searchQuery, 2)];
  51. default: return ['<', substr($searchQuery, 1)];
  52. }
  53. case '!':
  54. switch (substr($searchQuery, 1, 1)) {
  55. case '=': return ['!=', substr($searchQuery, 2)];
  56. default: return ['not like', substr($searchQuery, 1)];
  57. }
  58. default: {
  59. switch ($fieldType) {
  60. case 'xsd:number':
  61. case 'xsd:integer': return ['=', $searchQuery];
  62. default: {
  63. if (false !== strpos($searchQuery, '%')) return ['like', $searchQuery];
  64. $queryWhereBuilder = new SqlQueryWhereBuilder();
  65. return ['and'
  66. , array_map(function ($word) use ($fieldName) {
  67. return [$fieldName, 'like', "%{$word}%"];
  68. }, $queryWhereBuilder->splitQueryToWords($searchQuery)
  69. )
  70. ];
  71. }
  72. }
  73. return ['=', $searchQuery];
  74. }
  75. }
  76. }
  77. public function _parseGeomQuery($searchQuery) { // _sqlValueForGeomField($fldName, $fltrValue, $tblPrefix = 't')
  78. // example: BBOX:54.40993961633866,18.583889010112824,54.337945760687454,18.397121431987586
  79. DBG::log($searchQuery, 'string', "\$searchQuery");
  80. if ('BBOX:' == substr($searchQuery, 0, 5)) {
  81. $valParts = explode(',', substr($searchQuery, 5));
  82. if (4 !== count($valParts)) throw new Exception("Wrong BBOX query");
  83. $valParts = array_filter($valParts, 'is_numeric');
  84. if (4 !== count($valParts)) throw new Exception("Wrong BBOX query - expected 4 numeric values");
  85. $bounds = "POLYGON((
  86. {$valParts[3]} {$valParts[2]},
  87. {$valParts[3]} {$valParts[0]},
  88. {$valParts[1]} {$valParts[0]},
  89. {$valParts[1]} {$valParts[2]},
  90. {$valParts[3]} {$valParts[2]}
  91. ))";
  92. // for mysql 5.6 use ST_Contains() @see http://dev.mysql.com/doc/refman/5.6/en/spatial-relation-functions.html
  93. return [ 'Intersects', $bounds ];
  94. }
  95. else if ('GeometryType=' == substr($fltrValue, 0, 13)) {
  96. return [ 'GeometryType', substr($fltrValue, 13) ];
  97. }
  98. throw new Exception("Not implemented geometry query string"); // TODO:? return null;
  99. }
  100. public function _sqlValueForCsvNumericField($fldName, $fltrValue, $tblPrefix = 't') {
  101. $sqlFilter = false;
  102. if (is_numeric($fltrValue)) {
  103. $sqlFilter = "FIND_IN_SET('{$fltrValue}', `{$fldName}`)>0";
  104. } else if (false !== strpos($fltrValue, ' ')) {
  105. $sqlGlue = " or ";
  106. $fltrValues = $fltrValue;
  107. if ('&' == substr($fltrValues, 0, 1)) {
  108. $fltrValues = substr($fltrValues, 1);
  109. $sqlGlue = " and ";
  110. }
  111. $fltrValues = explode(' ', $fltrValues);
  112. $sqlNumericValues = array();
  113. foreach ($fltrValues as $fltrVal) {
  114. if (is_numeric($fltrVal)) {
  115. $sqlNumericValues[] = "FIND_IN_SET('{$fltrVal}', `{$fldName}`)>0";
  116. }
  117. }
  118. if (!empty($sqlNumericValues)) {
  119. $sqlFilter = "(" . implode($sqlGlue, $sqlNumericValues) . ")";
  120. }
  121. }
  122. return $sqlFilter;
  123. }
  124. public function getQuery() {
  125. if ($this->_query) return $this->_query;
  126. // $ds = $this->_acl->getDataSource(); // TODO: only for TableAcl // TODO: move _parseSqlWhere to this class
  127. $filtrIsInstance = [];
  128. $filtrIsNotInstance = [];
  129. $this->_query = ACL::query($this->_acl)
  130. ->isInstance($filtrIsInstance)
  131. ->isNotInstance($filtrIsNotInstance);
  132. // ->join($instanceTable, 'i', [ 'rawJoin' => "i.pk = t.{$sqlPrimaryKey} and i.idInstance = {$idInstance}" ])
  133. // $this->_query->where([
  134. // 'rawWhere' => $ds->_parseSqlWhere($params)
  135. // ]);
  136. foreach ($this->_params as $k => $v) {
  137. if ('f_' === substr($k, 0, 2) && strlen($k) > 3) {
  138. $fieldName = substr($k, 2);
  139. $fieldType = $this->_acl->getXsdFieldType($fieldName);
  140. list($comparisonSign, $value) = $this->parseQueryValue($fieldName, $v, $fieldType);
  141. DBG::log([ $fieldName, $fieldType, $comparisonSign, $value ], 'array', "parseQueryValue");
  142. $this->_query->where($fieldName, $comparisonSign, $value);
  143. } else if ('sf_' === substr($k, 0, 3) && strlen($k) > 4) {
  144. } else if ('ogc:Filter' == $k) {
  145. } else if ('primaryKey' == $k) {
  146. }
  147. }
  148. return $this->_query;
  149. }
  150. public function getTotal() {
  151. if ($this->_legacyMode) return $this->_acl->getTotal($this->_params);
  152. if (null !== $this->_total) return $this->_total;
  153. $this->_total = $this->getQuery()
  154. ->select([ 'rawSelect' => "count(1) as cnt" ])
  155. ->fetchValue();
  156. return $this->_total;
  157. }
  158. public function hasParam($key) { return !empty($this->_params[$key]); }
  159. public function getParam($key) { return V::get($key, '', $this->_params); }
  160. public function getItems() {
  161. if ($this->_legacyMode) return $this->_acl->getTotal($this->_params);
  162. // 'limit' => 10,
  163. // 'limitstart' => 0,
  164. // 'order_by' => 'ID',
  165. // 'order_dir' => 'desc',
  166. // TODO: sortBy from wfs query
  167. $sortBy = $this->hasParam('order_by')
  168. ? ( $this->hasParam('order_dir')
  169. ? $this->getParam('order_by') . " " . $this->getParam('order_dir')
  170. : $this->getParam('order_by')
  171. )
  172. : '';
  173. $limit = V::get('limit', 10, $this->_params, 'int');
  174. $offset = V::get('limitstart', 0, $this->_params, 'int');
  175. $ds = $this->_acl->getDataSource(); // TODO: only for TableAcl // TODO: move _parseSqlWhere to this class
  176. DBG::log(['params' => $this->_params, 'sortBy' => $sortBy, 'limit' => $limit, 'offset' => $offset], 'array', '$this->_params');
  177. return $this->getQuery()
  178. ->select([
  179. 'rawSelect' => $ds->_getSqlCols()
  180. ]) // TODO: fields
  181. ->select(!empty($this->_params['@instances']) ? '@instances' : null)
  182. ->limit($limit)
  183. ->offset($offset)
  184. ->orderBy($sortBy)
  185. ->fetchAll();
  186. }
  187. public function getItem($primaryKey) { throw new HttpException("Acl function " . __FUNCTION__ . " Not implemented", 501); }
  188. }