PageFinder::getQueryNativeField PHP Method

getQueryNativeField() protected method

TODO not all operators will work here, so may want to add some translation or filtering
protected getQueryNativeField ( DatabaseQuerySelect $query, Selector $selector, array $fields )
$query DatabaseQuerySelect
$selector Selector
$fields array
    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->wire('fields')->isNative($field)) {
                $subfield = $field;
                $field = 'children';
            }
            if ($field == 'child') {
                $field = 'children';
            }
            if (in_array($field, array('parent', 'parent_id', 'children'))) {
                if (strpos($field, 'parent') === 0 && (!$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;
                        }
                        $v = $this->wire('sanitizer')->pagePathName($v);
                        if (strpos($v, '/') === false) {
                            $v = "/{$v}";
                        }
                        // prevent a plain string with no slashes
                        // convert path to id
                        $parent = $this->wire('pages')->get($v);
                        if (!$parent instanceof NullPage) {
                            $values[$k] = $parent->id;
                        } else {
                            $values[$k] = null;
                        }
                    }
                    $field = 'parent_id';
                    if (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')->isNative($subfield)) {
                        $finder = new PageFinder();
                        $s = $field == 'children' ? '' : 'children.count>0, ';
                        $IDs = $finder->findIDs(new Selectors("include=all, {$s}{$subfield}{$operator}" . implode('|', $values)));
                        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', 'published'))) {
                        // prepare value for created, modified or published date fields
                        if (!ctype_digit($value)) {
                            $value = strtotime($value);
                        }
                        $value = date('Y-m-d H:i:s', $value);
                    }
                    if (in_array($field, array('id', 'parent_id', 'templates_id'))) {
                        $value = (int) $value;
                    }
                    $isName = $field === 'name' || strpos($field, 'name') === 0;
                    if ($isName && $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($this->wire('sanitizer')->pageName($word));
                            $s .= ($s ? ' AND ' : '') . "{$table}.{$field} RLIKE '" . '[[:<:]]' . $word . '[[:>:]]' . "'";
                        }
                    } else {
                        if ($isName && in_array($operator, array('%=', '^=', '$=', '%^=', '%$=', '*='))) {
                            // handle partial match to 'name' field
                            $value = $database->escapeStr($this->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 {
                                if ($isName) {
                                    $value = $this->wire('sanitizer')->pageName($value);
                                }
                                $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);
        //$this->nativeWheres[] = $SQL;
    }