buildQuery($params); // (view): $total = $queryFeatures->getTotal(); // (view): $items = $queryFeatures->getItems(); // example: @see TableAcl, TableAjax class AclQueryFeatures { public $_params; public $_acl; public $_query; public $_total; public $_legacyMode; public function __construct($acl, $params) { $this->_acl = $acl; $this->_params = $params; $this->_query = null; $this->_total = null; $this->_legacyMode = false; // TODO: _legacyMode = ($from instanceof simple schema or another programmed objects) } public function parseQueryValue($searchQuery, $fieldType = 'xsd:string') { if ('!NULL' === $searchQuery) return ['is not null', null]; if ('IS NOT NULL' === $searchQuery) return ['is not null', null]; if ('NULL' === $searchQuery) return ['is null', null]; if ('IS NULL' === $searchQuery) return ['is null', null]; switch ($fieldType) { case 'gml:PolygonPropertyType': case 'gml:PointPropertyType': case 'gml:LineStringPropertyType': case 'gml:GeometryPropertyType': return $this->_parseGeomQuery($searchQuery); // $sqlFilter = $this->_sqlValueForGeomField($fldName, $v, 't'); // if ('_CSV_NUM' == substr($fldName, -8)) { // if ($this->isCsvNumericField($fldName)) { // TODO: xsd type - p5:csv_num // $sqlFilter = $this->_sqlValueForCsvNumericField($fldName, $v, 't'); // if ($sqlFilter) $sql_where_and[] = $sqlFilter; // continue; // } } switch (substr($searchQuery, 0, 1)) { case '=': return ['=', substr($searchQuery, 1)]; case '>': switch (substr($searchQuery, 1, 1)) { case '=': return ['>=', substr($searchQuery, 2)]; default: return ['>', substr($searchQuery, 1)]; } case '<': switch (substr($searchQuery, 1, 1)) { case '=': return ['<=', substr($searchQuery, 2)]; case '>': return ['!=', substr($searchQuery, 2)]; default: return ['<', substr($searchQuery, 1)]; } case '!': switch (substr($searchQuery, 1, 1)) { case '=': return ['!=', substr($searchQuery, 2)]; default: return ['not like', substr($searchQuery, 1)]; } default: { if (false !== strpos($searchQuery, '%')) return ['like', $searchQuery]; if ('xsd:number' === $fieldType) return ['=', $searchQuery]; // if ($acl->isColTypeNumber($fieldName)) return ['=', $searchQuery]; // else { // $queryWhereBuilder = new SqlQueryWhereBuilder(); // $searchWords = $queryWhereBuilder->splitQueryToWords($v); // $sqlWhereWords = array(); // if (!empty($searchWords)) { // foreach ($searchWords as $word) { // $sqlWord = $this->_db->_($word); // $sqlWhereWords[] = "t.`{$fldName}` like '%{$sqlWord}%'"; // } // } // if (!empty($searchWords)) { // $sql_where_and[] = "(" . implode(" and ", $sqlWhereWords) . ")"; // } // } return ['=', $searchQuery]; } } } public function _parseGeomQuery($searchQuery) { // _sqlValueForGeomField($fldName, $fltrValue, $tblPrefix = 't') // example: BBOX:54.40993961633866,18.583889010112824,54.337945760687454,18.397121431987586 DBG::log($searchQuery, 'string', "\$searchQuery"); if ('BBOX:' == substr($searchQuery, 0, 5)) { $valParts = explode(',', substr($searchQuery, 5)); if (4 !== count($valParts)) throw new Exception("Wrong BBOX query"); $valParts = array_filter($valParts, 'is_numeric'); if (4 !== count($valParts)) throw new Exception("Wrong BBOX query - expected 4 numeric values"); $bounds = "POLYGON(( {$valParts[3]} {$valParts[2]}, {$valParts[3]} {$valParts[0]}, {$valParts[1]} {$valParts[0]}, {$valParts[1]} {$valParts[2]}, {$valParts[3]} {$valParts[2]} ))"; // for mysql 5.6 use ST_Contains() @see http://dev.mysql.com/doc/refman/5.6/en/spatial-relation-functions.html return [ 'Intersects', $bounds ]; } else if ('GeometryType=' == substr($fltrValue, 0, 13)) { return [ 'GeometryType', substr($fltrValue, 13) ]; } throw new Exception("Not implemented geometry query string"); // TODO:? return null; } public function _sqlValueForCsvNumericField($fldName, $fltrValue, $tblPrefix = 't') { $sqlFilter = false; if (is_numeric($fltrValue)) { $sqlFilter = "FIND_IN_SET('{$fltrValue}', `{$fldName}`)>0"; } else if (false !== strpos($fltrValue, ' ')) { $sqlGlue = " or "; $fltrValues = $fltrValue; if ('&' == substr($fltrValues, 0, 1)) { $fltrValues = substr($fltrValues, 1); $sqlGlue = " and "; } $fltrValues = explode(' ', $fltrValues); $sqlNumericValues = array(); foreach ($fltrValues as $fltrVal) { if (is_numeric($fltrVal)) { $sqlNumericValues[] = "FIND_IN_SET('{$fltrVal}', `{$fldName}`)>0"; } } if (!empty($sqlNumericValues)) { $sqlFilter = "(" . implode($sqlGlue, $sqlNumericValues) . ")"; } } return $sqlFilter; } public function getQuery() { if ($this->_query) return $this->_query; // $ds = $this->_acl->getDataSource(); // TODO: only for TableAcl // TODO: move _parseSqlWhere to this class $filtrIsInstance = []; $filtrIsNotInstance = []; $this->_query = ACL::query($this->_acl) ->isInstance($filtrIsInstance) ->isNotInstance($filtrIsNotInstance); // ->join($instanceTable, 'i', [ 'rawJoin' => "i.pk = t.{$sqlPrimaryKey} and i.idInstance = {$idInstance}" ]) // $this->_query->where([ // 'rawWhere' => $ds->_parseSqlWhere($params) // ]); foreach ($this->_params as $k => $v) { if ('f_' === substr($k, 0, 2) && strlen($k) > 3) { $fieldName = substr($k, 2); $fieldType = $this->_acl->getXsdFieldType($fieldName); list($comparisonSign, $value) = $this->parseQueryValue($v, $fieldType); DBG::log([ $fieldName, $fieldType, $comparisonSign, $value ], 'array', "parseQueryValue"); $this->_query->where($fieldName, $comparisonSign, $value); } else if ('sf_' === substr($k, 0, 3) && strlen($k) > 4) { } else if ('ogc:Filter' == $k) { } else if ('primaryKey' == $k) { } } return $this->_query; } public function getTotal() { if ($this->_legacyMode) return $this->_acl->getTotal($this->_params); if (null !== $this->_total) return $this->_total; $this->_total = $this->getQuery() ->select([ 'rawSelect' => "count(1) as cnt" ]) ->fetchValue(); return $this->_total; } public function hasParam($key) { return !empty($this->_params[$key]); } public function getParam($key) { return V::get($key, '', $this->_params); } public function getItems() { if ($this->_legacyMode) return $this->_acl->getTotal($this->_params); // 'limit' => 10, // 'limitstart' => 0, // 'order_by' => 'ID', // 'order_dir' => 'desc', // TODO: sortBy from wfs query $sortBy = $this->hasParam('order_by') ? ( $this->hasParam('order_dir') ? $this->getParam('order_by') . " " . $this->getParam('order_dir') : $this->getParam('order_by') ) : ''; $limit = V::get('limit', 10, $this->_params, 'int'); $offset = V::get('limitstart', 0, $this->_params, 'int'); $ds = $this->_acl->getDataSource(); // TODO: only for TableAcl // TODO: move _parseSqlWhere to this class DBG::log(['params' => $this->_params, 'sortBy' => $sortBy, 'limit' => $limit, 'offset' => $offset], 'array', '$this->_params'); return $this->getQuery() ->select([ 'rawSelect' => $ds->_getSqlCols() ]) // TODO: fields ->select(!empty($this->_params['@instances']) ? '@instances' : null) ->limit($limit) ->offset($offset) ->orderBy($sortBy) ->fetchAll(); } public function getItem($primaryKey) { throw new HttpException("Acl function " . __FUNCTION__ . " Not implemented", 501); } }