Initial commit
This commit is contained in:
223
engine/class.search.php
Normal file
223
engine/class.search.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
defined('ROOT_DIR') || exit;
|
||||
|
||||
class Search {
|
||||
public array $query;
|
||||
private string $sql_query = "";
|
||||
private array $variables = array();
|
||||
public int $count = 0;
|
||||
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
list($sql_query, $variables) = $this->query_construct();
|
||||
$this->sql_query = $sql_query;
|
||||
$this->variables = $variables;
|
||||
}
|
||||
|
||||
// Извлечение данных из базы
|
||||
public function collect($fill = true, $new = true)
|
||||
{
|
||||
global $b;
|
||||
$result = $b->db_query($this->sql_query, $this->variables, $new);
|
||||
if(!$fill) return $result;
|
||||
|
||||
$this->get_count();
|
||||
|
||||
$items = array();
|
||||
foreach ($result as $key => $item) {
|
||||
$class = new $item["item_class"]($item["item_id"]);
|
||||
$class->fill_main_parent($item["item_parent"]);
|
||||
$class->props_values = $this->search_props($item, $class);
|
||||
$items[] = $class;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
// Поиск параметров
|
||||
private function search_props($item, $class): array
|
||||
{
|
||||
$props = explode("-+-", $item["properties"]);
|
||||
$values = explode("-+-", $item["values"]);
|
||||
$result = array();
|
||||
|
||||
for ($i = 0; $i < count($props); $i++) {
|
||||
$prop = $props[$i];
|
||||
$value = $values[$i];
|
||||
if(!isset($class->props[$prop])) continue;
|
||||
$result[$prop] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Получить количество элементов
|
||||
public function get_count(){
|
||||
global $b;
|
||||
list($query_count, $variables) = $this->query_construct(true);
|
||||
$result = $b->db_query($query_count, $variables, false);
|
||||
$this->count = $result[0]["count"];
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Создание запроса
|
||||
private function query_construct($count = false): array
|
||||
{
|
||||
$variables = array();
|
||||
|
||||
$props = $this->query["props"];
|
||||
$params = $this->query["terms"] ?? array();
|
||||
|
||||
if(isset($this->query["class"])) $params["item_class"] = $this->query["class"];
|
||||
if(isset($this->query["parent_id"])) $params["item_parent"] = $this->query["parent_id"];
|
||||
|
||||
$limit = intval($this->query["limit"] ?? 184467440737);
|
||||
$offset = intval($this->query["offset"] ?? 0);
|
||||
|
||||
$order = $this->query_order($this->query["order"]);
|
||||
$order_inner = $this->query_order_inner($this->query["order"]);
|
||||
|
||||
$where_list = array();
|
||||
|
||||
list($where_props, $props_var) = $this->query_props($props);
|
||||
$variables = array_merge($variables, $props_var);
|
||||
|
||||
list($where, $where_var) = $this->query_where($params);
|
||||
$variables = array_merge($variables, $where_var);
|
||||
|
||||
if($where_props) $where_list[] = $where_props;
|
||||
if($where) $where_list[] = $where;
|
||||
|
||||
$return_fields = "COUNT(*) as count";
|
||||
$group_by = $join_props = $limit_query = $offset_query = "";
|
||||
|
||||
if(!$count){
|
||||
$return_fields = " `bive_items`.* ";
|
||||
$group_by = " GROUP BY `bive_items`.`item_id` ";
|
||||
$limit_query = " LIMIT $limit ";
|
||||
$offset_query = " OFFSET $offset ";
|
||||
|
||||
if(isset($params["item_class"])) {
|
||||
$return_fields = "`bive_items`.*, GROUP_CONCAT(p.prop_key SEPARATOR '-+-') AS properties, GROUP_CONCAT(p.prop_value SEPARATOR '-+-') AS `values`";
|
||||
$join_props = " JOIN `bive_items_props` p ON `bive_items`.`item_id` = p.`item_id` ";
|
||||
|
||||
$class = new $params["item_class"](0);
|
||||
if(!count($class->props)) {
|
||||
$return_fields = "`bive_items`.*";
|
||||
$group_by = "";
|
||||
$join_props = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$where_collect = $this->query_binary($where_list);
|
||||
$result_query = "SELECT $return_fields FROM `bive_items` $join_props $order_inner WHERE $where_collect $group_by $order $limit_query $offset_query;";
|
||||
return array($result_query, $variables);
|
||||
}
|
||||
|
||||
// Соединение таблиц для сортировки
|
||||
private function query_order_inner($order): string
|
||||
{
|
||||
if(!isset($order) || !isset($order["key"])) return "";
|
||||
$key = $order["key"];
|
||||
return "INNER JOIN `bive_items_props` AS `bip` ON `bive_items`.`item_id` = `bip`.`item_id` AND `bip`.`prop_key` = '$key'";
|
||||
}
|
||||
|
||||
// Сортировка
|
||||
private function query_order($order): string
|
||||
{
|
||||
if(!isset($order) || !isset($order["key"])) return "";
|
||||
$direction = $order["direction"] ?? "ASC";
|
||||
$type = $order["direction"] == "numeric" ? "CAST(`bip`.`prop_value` AS SIGNED)" : "`bip`.`prop_value`";
|
||||
return " ORDER BY $type $direction ";
|
||||
}
|
||||
|
||||
private function query_binary($array, $operator = "AND"): string
|
||||
{
|
||||
return "(" . implode(" " . mb_strtoupper($operator) . " ", $array) . ")";
|
||||
}
|
||||
|
||||
// Условия поиска по свойствам
|
||||
private function query_props($props, $operator = "and"): array
|
||||
{
|
||||
if(!$props || !count($props)) return array("", array());
|
||||
$sql = array();
|
||||
$variables = array();
|
||||
foreach ($props as $key => $prop) {
|
||||
if($key == "and") {
|
||||
list($query, $var) = $this->query_props($prop, "and");
|
||||
$sql[] = $query;
|
||||
$variables[] = $var;
|
||||
}else if($key == "or") {
|
||||
list($query, $var) = $this->query_props($prop, "or");
|
||||
$sql[] = $query;
|
||||
$variables[] = $var;
|
||||
} else {
|
||||
list($key, $prop, $not, $op) = $this->query_operators($key, $prop);
|
||||
$sql[] = $not . "EXISTS (
|
||||
SELECT 1
|
||||
FROM bive_items_props
|
||||
WHERE bive_items.item_id = bive_items_props.item_id
|
||||
AND bive_items_props.prop_key = '$key' AND bive_items_props.prop_value $op ?
|
||||
)";
|
||||
$variables[] = $prop;
|
||||
}
|
||||
}
|
||||
$string_query = $this->query_binary($sql, $operator);
|
||||
return array($string_query, $variables);
|
||||
}
|
||||
|
||||
// Условия поиска по предметам
|
||||
private function query_where($where, $operator = "and"): array
|
||||
{
|
||||
if(!$where || !count($where)) return array("", array());
|
||||
$sql = array();
|
||||
$variables = array();
|
||||
foreach ($where as $key => $prop) {
|
||||
if($key == "and") {
|
||||
list($query, $var) = $this->query_where($prop, "and");
|
||||
$sql[] = $query;
|
||||
$variables = array(...$variables, ...$var);
|
||||
}else if($key == "or") {
|
||||
list($query, $var) = $this->query_where($prop, "or");
|
||||
$sql[] = $query;
|
||||
$variables[] = array(...$variables, ...$var);
|
||||
} else {
|
||||
list($key, $prop, $not, $op) = $this->query_operators($key, $prop);
|
||||
$sql[] = "$not`bive_items`.`$key` $op ?";
|
||||
$variables[] = $prop;
|
||||
}
|
||||
}
|
||||
$string_query = $this->query_binary($sql, $operator);
|
||||
return array($string_query, $variables);
|
||||
}
|
||||
|
||||
private function query_operators($key, $prop): array
|
||||
{
|
||||
$not = "";
|
||||
$op = " = ";
|
||||
|
||||
// Добавление отрицания
|
||||
if(mb_substr($key, 0, 1) == "!") {
|
||||
$key = substr($key, 1);
|
||||
$not = " NOT ";
|
||||
}
|
||||
|
||||
// Добавление конструкции LIKE
|
||||
if(mb_substr($key, 0, 1) == "%") {
|
||||
$prop = "%" . $prop;
|
||||
$key = substr($key, 1);
|
||||
$op = " LIKE ";
|
||||
}
|
||||
|
||||
// Добавление конструкции LIKE
|
||||
if(mb_substr($key, -1) == "%") {
|
||||
$prop = $prop . "%";
|
||||
$key = substr($key, 0, -1);
|
||||
$op = " LIKE ";
|
||||
}
|
||||
|
||||
return array($key, $prop, $not, $op);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user