Поисковая система

This commit is contained in:
Konstantin
2026-05-31 11:17:00 +03:00
parent 6c8a012702
commit 2aba593aa3
8 changed files with 509 additions and 324 deletions
+89 -104
View File
@@ -74,6 +74,10 @@ class ModelCatalogProduct extends Model
{
$sql = 'SELECT p.product_id, (SELECT AVG(rating) AS total FROM ' . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating, (SELECT price FROM " . DB_PREFIX . "product_discount pd2 WHERE pd2.product_id = p.product_id AND pd2.customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "' AND pd2.quantity = '1' AND ((pd2.date_start = '0000-00-00' OR pd2.date_start < NOW()) AND (pd2.date_end = '0000-00-00' OR pd2.date_end > NOW())) ORDER BY pd2.priority ASC, pd2.price ASC LIMIT 1) AS discount, (SELECT price FROM " . DB_PREFIX . "product_special ps WHERE ps.product_id = p.product_id AND ps.customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "' AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND (ps.date_end = '0000-00-00' OR ps.date_end > NOW())) ORDER BY ps.priority ASC, ps.price ASC LIMIT 1) AS special";
if (!empty($data['filter_name'])) {
$sql .= ', ' . $this->getSearchRelevanceSql($data['filter_name']) . ' AS relevance';
}
if (!empty($data['filter_category_id'])) {
if (!empty($data['filter_sub_category'])) {
$sql .= ' FROM ' . DB_PREFIX . 'category_path cp LEFT JOIN ' . DB_PREFIX . 'product_to_category p2c ON (cp.category_id = p2c.category_id)';
@@ -112,57 +116,7 @@ class ModelCatalogProduct extends Model
}
}
if (!empty($data['filter_name']) || !empty($data['filter_tag'])) {
$sql .= ' AND (';
if (!empty($data['filter_name'])) {
$implode = array();
$words = explode(' ', trim(preg_replace('/\s+/', ' ', $data['filter_name'])));
foreach ($words as $word) {
$implode[] = "pd.name LIKE '%" . $this->db->escape($word) . "%'";
}
if ($implode) {
$sql .= ' ' . implode(' AND ', $implode) . '';
}
if (!empty($data['filter_description'])) {
$sql .= " OR pd.description LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
}
}
if (!empty($data['filter_name']) && !empty($data['filter_tag'])) {
$sql .= ' OR ';
}
if (!empty($data['filter_tag'])) {
$implode = array();
$words = explode(' ', trim(preg_replace('/\s+/', ' ', $data['filter_tag'])));
foreach ($words as $word) {
$implode[] = "pd.tag LIKE '%" . $this->db->escape($word) . "%'";
}
if ($implode) {
$sql .= ' ' . implode(' AND ', $implode) . '';
}
}
if (!empty($data['filter_name'])) {
$sql .= " OR LCASE(p.model) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.sku) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.upc) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.ean) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.jan) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.isbn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.mpn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
}
$sql .= ')';
}
$sql .= $this->getSearchConditionSql($data);
if (!empty($data['filter_manufacturer_id'])) {
$sql .= " AND p.manufacturer_id = '" . (int) $data['filter_manufacturer_id'] . "'";
@@ -177,10 +131,13 @@ class ModelCatalogProduct extends Model
'p.price',
'rating',
'p.sort_order',
'p.date_added'
'p.date_added',
'relevance'
);
if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
if (isset($data['sort']) && $data['sort'] == 'relevance' && !empty($data['filter_name'])) {
$sql .= ' ORDER BY relevance';
} elseif (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
if ($data['sort'] == 'pd.name' || $data['sort'] == 'p.model') {
$sql .= ' ORDER BY LCASE(' . $data['sort'] . ')';
} elseif ($data['sort'] == 'p.price') {
@@ -501,57 +458,7 @@ class ModelCatalogProduct extends Model
}
}
if (!empty($data['filter_name']) || !empty($data['filter_tag'])) {
$sql .= ' AND (';
if (!empty($data['filter_name'])) {
$implode = array();
$words = explode(' ', trim(preg_replace('/\s+/', ' ', $data['filter_name'])));
foreach ($words as $word) {
$implode[] = "pd.name LIKE '%" . $this->db->escape($word) . "%'";
}
if ($implode) {
$sql .= ' ' . implode(' AND ', $implode) . '';
}
if (!empty($data['filter_description'])) {
$sql .= " OR pd.description LIKE '%" . $this->db->escape($data['filter_name']) . "%'";
}
}
if (!empty($data['filter_name']) && !empty($data['filter_tag'])) {
$sql .= ' OR ';
}
if (!empty($data['filter_tag'])) {
$implode = array();
$words = explode(' ', trim(preg_replace('/\s+/', ' ', $data['filter_tag'])));
foreach ($words as $word) {
$implode[] = "pd.tag LIKE '%" . $this->db->escape($word) . "%'";
}
if ($implode) {
$sql .= ' ' . implode(' AND ', $implode) . '';
}
}
if (!empty($data['filter_name'])) {
$sql .= " OR LCASE(p.model) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.sku) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.upc) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.ean) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.jan) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.isbn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
$sql .= " OR LCASE(p.mpn) = '" . $this->db->escape(utf8_strtolower($data['filter_name'])) . "'";
}
$sql .= ')';
}
$sql .= $this->getSearchConditionSql($data);
if (!empty($data['filter_manufacturer_id'])) {
$sql .= " AND p.manufacturer_id = '" . (int) $data['filter_manufacturer_id'] . "'";
@@ -562,6 +469,84 @@ class ModelCatalogProduct extends Model
return $query->row['total'];
}
private function getSearchConditionSql($data)
{
$groups = array();
if (!empty($data['filter_name'])) {
$conditions = array();
foreach ($this->getSearchWords($data['filter_name']) as $word) {
$word = $this->db->escape($word);
$matches = array(
"pd.name LIKE '%" . $word . "%'",
"p.model LIKE '%" . $word . "%'",
"p.sku LIKE '%" . $word . "%'",
"p.upc LIKE '%" . $word . "%'",
"p.ean LIKE '%" . $word . "%'",
"p.jan LIKE '%" . $word . "%'",
"p.isbn LIKE '%" . $word . "%'",
"p.mpn LIKE '%" . $word . "%'",
$this->getAttributeMatchSql($word)
);
if (!empty($data['filter_description'])) {
$matches[] = "pd.description LIKE '%" . $word . "%'";
}
$conditions[] = '(' . implode(' OR ', $matches) . ')';
}
if ($conditions) {
$groups[] = '(' . implode(' AND ', $conditions) . ')';
}
}
if (!empty($data['filter_tag'])) {
$conditions = array();
foreach ($this->getSearchWords($data['filter_tag']) as $word) {
$conditions[] = "pd.tag LIKE '%" . $this->db->escape($word) . "%'";
}
if ($conditions) {
$groups[] = '(' . implode(' AND ', $conditions) . ')';
}
}
return $groups ? ' AND (' . implode(' OR ', $groups) . ')' : '';
}
private function getSearchRelevanceSql($search)
{
$search = utf8_strtolower(trim(preg_replace('/\s+/', ' ', $search)));
$escaped_search = $this->db->escape($search);
$sql = "(CASE WHEN LCASE(pd.name) = '" . $escaped_search . "' THEN 120 WHEN LCASE(pd.name) LIKE '" . $escaped_search . "%' THEN 80 WHEN LCASE(pd.name) LIKE '%" . $escaped_search . "%' THEN 50 ELSE 0 END";
$sql .= " + CASE WHEN LCASE(p.model) = '" . $escaped_search . "' THEN 110 WHEN LCASE(p.model) LIKE '" . $escaped_search . "%' THEN 70 WHEN LCASE(p.model) LIKE '%" . $escaped_search . "%' THEN 40 ELSE 0 END";
$sql .= " + CASE WHEN LCASE(p.sku) = '" . $escaped_search . "' OR LCASE(p.upc) = '" . $escaped_search . "' OR LCASE(p.ean) = '" . $escaped_search . "' OR LCASE(p.jan) = '" . $escaped_search . "' OR LCASE(p.isbn) = '" . $escaped_search . "' OR LCASE(p.mpn) = '" . $escaped_search . "' THEN 90 ELSE 0 END";
foreach ($this->getSearchWords($search) as $word) {
$word = $this->db->escape($word);
$sql .= " + CASE WHEN pd.name LIKE '%" . $word . "%' THEN 14 ELSE 0 END";
$sql .= " + CASE WHEN p.model LIKE '%" . $word . "%' THEN 10 ELSE 0 END";
$sql .= ' + CASE WHEN ' . $this->getAttributeMatchSql($word) . ' THEN 5 ELSE 0 END';
}
return $sql . ')';
}
private function getAttributeMatchSql($word)
{
$language_id = (int) $this->config->get('config_language_id');
return 'EXISTS (SELECT 1 FROM ' . DB_PREFIX . 'product_attribute pa LEFT JOIN ' . DB_PREFIX . "attribute_description ad ON (pa.attribute_id = ad.attribute_id AND ad.language_id = '" . $language_id . "') WHERE pa.product_id = p.product_id AND pa.language_id = '" . $language_id . "' AND (pa.text LIKE '%" . $word . "%' OR ad.name LIKE '%" . $word . "%'))";
}
private function getSearchWords($search)
{
return array_filter(explode(' ', trim(preg_replace('/\s+/', ' ', $search))), 'strlen');
}
public function getProfile($product_id, $recurring_id)
{
$query = $this->db->query('SELECT * FROM ' . DB_PREFIX . 'recurring r JOIN ' . DB_PREFIX . "product_recurring pr ON (pr.recurring_id = r.recurring_id AND pr.product_id = '" . (int) $product_id . "') WHERE pr.recurring_id = '" . (int) $recurring_id . "' AND status = '1' AND pr.customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "'");