Files
BiveEngine/engine/class.search.php
2025-12-24 19:19:01 +03:00

223 lines
7.8 KiB
PHP

<?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);
}
}