protected function postProcessQuery($parentQuery) { if (count($this->extraOrSelectors)) { // there were embedded OR selectors where one of them must match // i.e. id>0, field=(selector string), field=(selector string) // in the above example at least one 'field' must match // the 'field' portion is used only as a group name and isn't // actually used as part of the resulting query or than to know // what groups should be OR'd together $sqls = array(); foreach ($this->extraOrSelectors as $groupName => $selectorGroup) { $n = 0; $sql = "\tpages.id IN (\n"; foreach ($selectorGroup as $selectors) { $pageFinder = new PageFinder(); $query = $pageFinder->find($selectors, array('returnQuery' => true, 'returnVerbose' => false, 'findAll' => true)); if ($n > 0) { $sql .= " \n\tOR pages.id IN (\n"; } $query->set('groupby', array()); $query->set('select', array('pages.id')); $query->set('orderby', array()); // foreach($this->nativeWheres as $where) $query->where($where); // doesn't seem to speed anything up, MySQL must already optimize for this $sql .= tabIndent("\t\t" . $query->getQuery() . "\n)", 2); $n++; } $sqls[] = $sql; } if (count($sqls)) { $sql = implode(" \n) AND (\n ", $sqls); $parentQuery->where("(\n{$sql}\n)"); } } /* Possibly move existing subselectors to work like this rather than how they currently are if(count($this->extraSubSelectors)) { $sqls = array(); foreach($this->extraSubSelectors as $fieldName => $selectorGroup) { $fieldName = $this->wire('database')->escapeCol($fieldName); $n = 0; $sql = "\tpages.id IN (\n"; foreach($selectorGroup as $selectors) { $pageFinder = new PageFinder(); $query = $pageFinder->find($selectors, array('returnQuery' => true, 'returnVerbose' => false)); if($n > 0) $sql .= " \n\tAND pages.id IN (\n"; $query->set('groupby', array()); $query->set('select', array('pages.id')); $query->set('orderby', array()); // foreach($this->nativeWheres as $where) $query->where($where); $sql .= tabIndent("\t\t" . $query->getQuery() . "\n)", 2); $n++; } $sqls[] = $sql; } if(count($sqls)) { $sql = implode(" \n) AND (\n ", $sqls); $parentQuery->where("(\n$sql\n)"); } } */ }
/** * Special case when field is native to the pages table * * TODO not all operators will work here, so may want to add some translation or filtering * */ protected function getQueryNativeField(DatabaseQuerySelect $query, $selector, $fields) { $value = $selector->value; $values = is_array($value) ? $value : array($value); $SQL = ''; $database = $this->wire('database'); foreach ($fields as $field) { // the following fields are defined in each iteration here because they may be modified in the loop $table = "pages"; $operator = $selector->operator; $subfield = ''; $IDs = array(); // populated in special cases where we can just match parent IDs $sql = ''; if (strpos($field, '.')) { list($field, $subfield) = explode('.', $field); } if (!$this->getFuel('fields')->isNativeName($field)) { $subfield = $field; $field = 'children'; } if ($field == 'child') { $field = 'children'; } if (in_array($field, array('parent', 'parent_id', 'children'))) { if (!$subfield || in_array($subfield, array('id', 'path', 'url'))) { // match by location (id or path) // convert parent fields like '/about/company/history' to the equivalent ID foreach ($values as $k => $v) { if (ctype_digit("{$v}")) { continue; } // convert path to id $parent = $this->fuel('pages')->get($v); if (!$parent instanceof NullPage) { $values[$k] = $parent->id; } else { $values[$k] = null; } } $field = 'parent_id'; if ($field == 'parent_id' && count($values) == 1 && $selector->getOperator() === '=') { $this->parent_id = reset($values); } } else { // matching by a parent's native or custom field (subfield) if (!$this->wire('fields')->isNativeName($subfield)) { $finder = new PageFinder(); $s = $field == 'children' ? '' : 'children.count>0, '; $matches = $finder->find(new Selectors("include=all, {$s}{$subfield}{$operator}" . implode('|', $values))); foreach ($matches as $match) { $IDs[] = (int) $match['id']; } if (!count($IDs)) { $IDs[] = -1; } // forced non match } else { // native static $n = 0; if ($field == 'children') { $table = "_children_native" . ++$n; $query->join("pages AS {$table} ON {$table}.parent_id=pages.id"); } else { $table = "_parent_native" . ++$n; $query->join("pages AS {$table} ON pages.parent_id={$table}.id"); } $field = $subfield; } } } if (count($IDs)) { // parentIDs are IDs found via another query, and we don't need to match anything other than the parent ID $in = $selector->not ? "NOT IN" : "IN"; $sql .= in_array($field, array('parent', 'parent_id')) ? "{$table}.parent_id " : "{$table}.id "; $sql .= "{$in}(" . implode(',', $IDs) . ")"; } else { foreach ($values as $value) { if (is_null($value)) { // an invalid/unknown walue was specified, so make sure it fails $sql .= "1>2"; continue; } if (in_array($field, array('templates_id', 'template'))) { // convert templates specified as a name to the numeric template ID // allows selectors like 'template=my_template_name' $field = 'templates_id'; if (count($values) == 1 && $selector->getOperator() === '=') { $this->templates_id = reset($values); } if (!ctype_digit("{$value}")) { $value = ($template = $this->fuel('templates')->get($value)) ? $template->id : 0; } } if (in_array($field, array('created', 'modified'))) { // prepare value for created or modified date fields if (!ctype_digit($value)) { $value = strtotime($value); } $value = date('Y-m-d H:i:s', $value); } if ($field == 'name' && $operator == '~=') { // handle one or more space-separated full words match to 'name' field in any order $s = ''; foreach (explode(' ', $value) as $word) { $word = $database->escapeStr(wire('sanitizer')->pageName($word)); $s .= ($s ? ' AND ' : '') . "{$table}.{$field} RLIKE '" . '[[:<:]]' . $word . '[[:>:]]' . "'"; } } else { if ($field == 'name' && in_array($operator, array('%=', '^=', '$=', '%^=', '%$=', '*='))) { // handle partial match to 'name' field $value = $database->escapeStr(wire('sanitizer')->pageName($value)); if ($operator == '^=' || $operator == '%^=') { $value = "{$value}%"; } else { if ($operator == '$=' || $operator == '%$=') { $value = "%{$value}"; } else { $value = "%{$value}%"; } } $s = "{$table}.{$field} LIKE '{$value}'"; } else { if (!$database->isOperator($operator)) { throw new PageFinderSyntaxException("Operator '{$operator}' is not supported for '{$field}'."); } else { $value = $database->escapeStr($value); $s = "{$table}." . $field . $operator . (ctype_digit("{$value}") && $field != 'name' ? (int) $value : "'{$value}'"); } } } if ($selector->not) { $s = "NOT ({$s})"; } if ($operator == '!=' || $selector->not) { $sql .= $sql ? " AND {$s}" : "{$s}"; } else { $sql .= $sql ? " OR {$s}" : "{$s}"; } } } if ($sql) { if ($SQL) { $SQL .= " OR ({$sql})"; } else { $SQL .= "({$sql})"; } } } if (count($fields) > 1) { $SQL = "({$SQL})"; } $query->where($SQL); }