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

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
@@ -90,6 +90,7 @@ foreach(['name','email','telephone','address','open', 'comment'] as $item){
$data['shopping_cart'] = $this->url->link('checkout/cart');
$data['checkout'] = $this->url->link('checkout/checkout', '', true);
$data['contact'] = $this->url->link('information/contact');
$data['search_url'] = $this->url->link('product/search');
$data['telephone'] = $this->config->get('config_telephone');
$data['language'] = $this->load->controller('common/language');
+92 -6
View File
@@ -1,14 +1,100 @@
<?php
class ControllerProductSearch extends Controller {
public function index() {
$this->load->model('catalog/category');
$this->load->language('product/search');
$this->load->model('catalog/product');
$this->load->model('tool/image');
$categories = $this->model_catalog_category->getCategories(0);
$search = isset($this->request->get['search']) ? trim((string)$this->request->get['search']) : '';
$search = utf8_substr($search, 0, 120);
$page = isset($this->request->get['page']) ? max(1, (int)$this->request->get['page']) : 1;
$limit = (int)$this->config->get('theme_' . $this->config->get('config_theme') . '_product_limit');
if ($categories) {
$this->response->redirect($this->url->link('product/category', 'path=' . $categories[0]['category_id']));
} else {
$this->response->redirect($this->url->link('common/home'));
if ($limit < 1) {
$limit = 12;
}
$this->document->setTitle($search ? sprintf($this->language->get('heading_results'), $search) : $this->language->get('heading_title'));
$this->document->setRobots('noindex,follow');
$data['breadcrumbs'] = array(
array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/home')
),
array(
'text' => $this->language->get('heading_title'),
'href' => $this->url->link('product/search')
)
);
$data['heading_title'] = $this->language->get('heading_title');
$data['text_search_eyebrow'] = $this->language->get('text_search_eyebrow');
$data['text_search_hint'] = $this->language->get('text_search_hint');
$data['text_search_results'] = $this->language->get('text_search_results');
$data['text_search_for'] = $this->language->get('text_search_for');
$data['text_found'] = $this->language->get('text_found');
$data['text_start_search'] = $this->language->get('text_start_search');
$data['text_search_note'] = $this->language->get('text_search_note');
$data['text_empty'] = $this->language->get('text_empty');
$data['text_empty_hint'] = $this->language->get('text_empty_hint');
$data['text_keyword'] = $this->language->get('text_keyword');
$data['entry_search'] = $this->language->get('entry_search');
$data['button_search'] = $this->language->get('button_search');
$data['price_prefix'] = $this->language->get('price_prefix');
$data['search'] = $search;
$data['search_url'] = $this->url->link('product/search');
$data['products'] = array();
$data['product_total'] = 0;
$data['pagination'] = '';
$data['results'] = '';
if ($search !== '') {
$filter_data = array(
'filter_name' => $search,
'sort' => 'relevance',
'order' => 'DESC',
'start' => ($page - 1) * $limit,
'limit' => $limit
);
$data['product_total'] = $this->model_catalog_product->getTotalProducts($filter_data);
$results = $this->model_catalog_product->getProducts($filter_data);
foreach ($results as $result) {
$image = $this->model_tool_image->resize($result['image'] ? $result['image'] : 'placeholder.png', $this->config->get('theme_' . $this->config->get('config_theme') . '_image_product_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_product_height'));
$product_images = $this->model_catalog_product->getProductImages($result['product_id']);
$additional_image = $product_images ? $this->model_tool_image->resize($product_images[0]['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_product_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_product_height')) : false;
$data['products'][] = array(
'product_id' => $result['product_id'],
'thumb' => $image,
'additional_thumb' => $additional_image,
'price_n' => $result['price'],
'price_2' => $this->currency->format($this->tax->calculate($result['price_2'], $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']),
'price_2_n' => $result['price_2'],
'price_3' => $this->currency->format($this->tax->calculate($result['price_3'], $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']),
'name' => $result['name'],
'min_price' => $this->currency->format($this->tax->calculate(min(array($result['price'], $result['price_2'])), $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']),
'href' => $this->url->link('product/product', 'product_id=' . $result['product_id'])
);
}
$pagination = new Pagination();
$pagination->total = $data['product_total'];
$pagination->page = $page;
$pagination->limit = $limit;
$pagination->url = $this->url->link('product/search', 'search=' . rawurlencode($search) . '&page={page}');
$data['pagination'] = $pagination->render();
$data['results'] = sprintf($this->language->get('text_pagination'), ($data['product_total']) ? (($page - 1) * $limit) + 1 : 0, ((($page - 1) * $limit) > ($data['product_total'] - $limit)) ? $data['product_total'] : ((($page - 1) * $limit) + $limit), $data['product_total'], ceil($data['product_total'] / $limit));
}
$data['content_top'] = $this->load->controller('common/content_top');
$data['content_bottom'] = $this->load->controller('common/content_bottom');
$data['footer'] = $this->load->controller('common/footer');
$data['header'] = $this->load->controller('common/header');
$this->response->setOutput($this->load->view('product/search', $data));
}
}
+11 -2
View File
@@ -4,14 +4,23 @@
// Heading
$_['heading_title'] = 'Поиск';
$_['heading_results'] = 'Поиск: %s';
$_['heading_tag'] = 'По тегу - ';
// Text
$_['text_search'] = 'Товары, соответствующие критериям поиска';
$_['text_keyword'] = 'Ключевые слова';
$_['text_keyword'] = 'Название, модель или характеристика';
$_['text_search_eyebrow'] = 'Каталог Dominik';
$_['text_search_hint'] = 'Найдите нужную модель по названию, артикулу или характеристикам.';
$_['text_search_results'] = 'Результаты поиска';
$_['text_search_for'] = 'По запросу «%s»';
$_['text_found'] = 'Найдено товаров: %d';
$_['text_start_search'] = 'Что будем искать?';
$_['text_search_note'] = 'Например: кружево, длинный рукав или название модели.';
$_['text_empty_hint'] = 'Попробуйте изменить запрос: сократить его или проверить написание.';
$_['text_category'] = 'Все категории';
$_['text_sub_category'] = 'Поиск в подкатегориях';
$_['text_empty'] = 'Нет товаров, которые соответствуют критериям поиска.';
$_['text_empty'] = 'По вашему запросу ничего не найдено';
$_['text_quantity'] = 'Кол-во:';
$_['text_manufacturer'] = 'Производитель:';
$_['text_model'] = 'Код Товара:';
+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') . "'");
@@ -97,6 +97,15 @@ header .menu li a {
header .menu svg{
}
.header-search__icon,
a:hover .header-search__icon,
a:hover .header-search__icon path {
fill: none !important;
}
a:hover .header-search__icon circle,
a:hover .header-search__icon path {
stroke: var(--color-secondary);
}
header .menu>li:first-child {
padding-left: 0;
@@ -475,6 +484,190 @@ section{
border-color: #000
}
.search-modal__body {
padding: 42px 46px 48px;
}
.search-modal__intro {
max-width: 560px;
margin-bottom: 28px;
}
.search-modal__eyebrow,
.search-page__eyebrow {
display: block;
color: var(--color-dark-gray);
font-size: 11px;
letter-spacing: 0.18em;
line-height: 1.4;
text-transform: uppercase;
}
.search-modal__intro h2 {
margin: 9px 0 8px;
color: #14142b;
font-size: clamp(30px, 4vw, 43px);
font-weight: 400;
line-height: 1.08;
}
.search-modal__intro p {
margin: 0;
color: rgba(20, 20, 43, 0.68);
}
.search-modal__field,
.search-page__form {
display: flex;
border-bottom: 1px solid #14142b;
}
.search-modal__field input,
.search-page__form input {
width: 100%;
padding: 13px 0;
border: 0;
background: transparent;
color: #14142b;
font: inherit;
font-size: 18px;
outline: 0;
}
.search-modal__field button,
.search-page__form button {
display: inline-flex;
flex: 0 0 auto;
gap: 9px;
align-items: center;
padding: 12px 0 12px 18px;
border: 0;
background: transparent;
color: #14142b;
font: inherit;
text-transform: uppercase;
}
.search-modal__field button:hover,
.search-page__form button:hover {
color: var(--color-dark-gray);
}
.search-page {
min-height: 62vh;
color: #14142b;
}
.search-page__hero {
padding: 34px 0 54px;
background: linear-gradient(180deg, #f8f6f5 0, #fff 100%);
}
.search-page__breadcrumb {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 0;
margin: 0 0 42px;
list-style: none;
color: rgba(20, 20, 43, 0.58);
font-size: 14px;
}
.search-page__breadcrumb span {
margin-left: 4px;
}
.search-page__intro {
max-width: 760px;
}
.search-page__intro h1 {
margin: 10px 0 12px;
font-size: clamp(44px, 6.3vw, 88px);
font-weight: 400;
line-height: 1;
}
.search-page__intro p {
margin: 0;
color: rgba(20, 20, 43, 0.68);
font-size: 18px;
line-height: 1.55;
}
.search-page__form {
max-width: 920px;
margin-top: 37px;
}
.search-page__form input {
padding: 17px 0;
font-size: 21px;
}
.search-page__results {
padding: 12px 0 76px;
}
.search-page__summary {
display: flex;
gap: 20px;
align-items: end;
justify-content: space-between;
padding: 28px 0 32px;
}
.search-page__summary h2 {
margin: 8px 0 0;
font-size: clamp(26px, 3vw, 38px);
font-weight: 400;
line-height: 1.15;
}
.search-page__count {
color: rgba(20, 20, 43, 0.58);
white-space: nowrap;
}
.search-page__pagination {
display: flex;
gap: 20px;
align-items: center;
justify-content: space-between;
padding-top: 32px;
}
.search-page__empty {
max-width: 740px;
padding: 52px 0 24px;
}
.search-page__empty h2 {
margin: 0 0 12px;
font-size: clamp(27px, 3vw, 40px);
font-weight: 400;
}
.search-page__empty p {
margin: 0;
color: rgba(20, 20, 43, 0.68);
font-size: 18px;
line-height: 1.55;
}
@media (max-width: 767px) {
.search-modal__body {
padding: 38px 24px 32px;
}
.search-modal__field input,
.search-page__form input {
font-size: 16px;
}
.search-page__hero {
padding: 24px 0 38px;
}
.search-page__breadcrumb {
margin-bottom: 29px;
}
.search-page__intro h1 {
font-size: 50px;
}
.search-page__intro p,
.search-page__empty p {
font-size: 16px;
}
.search-page__form {
margin-top: 27px;
}
.search-page__form button span {
display: none;
}
.search-page__summary,
.search-page__pagination {
display: block;
}
.search-page__count,
.search-page__pagination > div + div {
margin-top: 12px;
}
}
.fav-btn {
position: absolute;
top: 20px;
@@ -1,3 +1,52 @@
function submitProductSearch(form) {
var input = $(form).find('input[name="search"]');
var search = $.trim(input.val());
if (!search) {
input.trigger('focus');
return false;
}
var url = $(form).data('search-url');
var separator = url.indexOf('?') === -1 ? '?' : '&';
window.location.href = url + separator + 'search=' + encodeURIComponent(search);
return false;
}
function openSearch(el) {
var form = $('<form>').addClass('search-modal__form').attr('data-search-url', $(el).data('search-url'));
var intro = $('<div class="search-modal__intro">').append(
'<span class="search-modal__eyebrow">Каталог Dominik</span>',
'<h2>Найти платье</h2>',
'<p>Введите название, модель или характеристику.</p>'
);
var field = $('<div class="search-modal__field">').append(
'<input type="search" name="search" placeholder="Например: кружево или длинный рукав" autocomplete="off" aria-label="Поиск">',
'<button type="submit" aria-label="Найти"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="11" cy="11" r="6.5" stroke="currentColor" stroke-width="1.5"/><path d="M16 16L21 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg></button>'
);
form.append(intro, field);
form.on('submit', function() {
return submitProductSearch(this);
});
modal.create({
modal: form,
width: 760,
class: {
'modal-body': 'search-modal__body'
},
callback: {
show: function() {
setTimeout(function() {
form.find('input[name="search"]').trigger('focus');
}, 200);
}
}
});
}
function NextPage(page, elem, btn){
console.log(page);
@@ -82,6 +82,11 @@
<div class="col d-flex justify-content-end">
<ul class="menu">
<li><a href="{{ search_url }}" data-search-url="{{ search_url }}" onclick="openSearch(this);return false;"><svg class="header-search__icon" width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="11" cy="11" r="6.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M16 16L21 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span class="d-none d-lg-inline-block ms-2">Поиск</span></a></li>
<li><a href="tel:{{ config_telephone }}"><svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.1708 16.415C13.6356 16.415 14.5982 16.0216 15.4436 15.0673C15.5106 15.0003 15.5776 14.925 15.6361 14.8581C16.1384 14.2972 16.3728 13.7448 16.3728 13.2174C16.3728 12.6148 16.0212 12.0539 15.2762 11.5349L12.8404 9.84406C12.0954 9.32514 11.2081 9.26656 10.505 9.96128L9.8605 10.6058C9.668 10.7983 9.50057 10.8067 9.31643 10.6895C8.86443 10.4049 7.952 9.60971 7.33257 8.99028C6.67969 8.34578 6.0519 7.62592 5.71708 7.09856C5.60826 6.90604 5.61663 6.747 5.80915 6.55448L6.44531 5.90995C7.14843 5.20682 7.08984 4.31955 6.57087 3.57457L4.88002 1.13874C4.36105 0.393764 3.80022 0.0505719 3.19754 0.0422014C2.6702 0.0338308 2.11774 0.276577 1.55692 0.778806C1.48159 0.84577 1.41462 0.904363 1.34766 0.962963C0.393415 1.81676 0 2.77936 0 4.22747C0 6.62144 1.47321 9.53442 4.1769 12.2381C6.86384 14.925 9.78514 16.415 12.1708 16.415ZM12.1791 15.1259C10.0446 15.1678 7.3075 13.5271 5.13951 11.3676C2.9548 9.19121 1.23884 6.36196 1.28069 4.22747C1.29744 3.30671 1.62389 2.51151 2.27679 1.94231C2.33538 1.89209 2.37723 1.85023 2.43583 1.80838C2.68694 1.59075 2.9548 1.47356 3.19754 1.47356C3.44029 1.47356 3.65792 1.56564 3.81696 1.81676L5.44085 4.25258C5.61663 4.51207 5.63337 4.80503 5.37389 5.06452L4.63728 5.80113C4.05971 6.3787 4.10156 7.08182 4.52009 7.64263C4.99721 8.28721 5.82589 9.22471 6.47042 9.86085C7.10659 10.5053 8.11943 11.4094 8.77229 11.8948C9.33314 12.3134 10.0363 12.3553 10.6139 11.7777L11.3504 11.0411C11.6099 10.7816 11.8945 10.7983 12.1624 10.9658L14.5982 12.5896C14.8409 12.7571 14.9414 12.9663 14.9414 13.2174C14.9414 13.4602 14.8242 13.7281 14.6066 13.9791C14.5564 14.0377 14.5229 14.0796 14.4726 14.1382C13.9034 14.7911 13.1083 15.1091 12.1791 15.1259Z" fill="black" fill-opacity="0.85"/>
</svg>
@@ -1,212 +1,69 @@
{{ header }}
<div id="product-search" class="container">
<ul class="breadcrumb">
{{ content_top }}
<main class="search-page">
<section class="search-page__hero">
<div class="container-fluid">
<ul class="search-page__breadcrumb">
{% for breadcrumb in breadcrumbs %}
<li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
<li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a>{% if not loop.last %}<span>/</span>{% endif %}</li>
{% endfor %}
</ul>
<div class="row">{{ column_left }}
{% if column_left and column_right %}
{% set class = 'col-sm-6' %}
{% elseif column_left or column_right %}
{% set class = 'col-sm-9' %}
{% else %}
{% set class = 'col-sm-12' %}
{% endif %}
<div id="content" class="{{ class }}">{{ content_top }}
<div class="search-page__intro">
<span class="search-page__eyebrow">{{ text_search_eyebrow }}</span>
<h1>{{ heading_title }}</h1>
<label class="control-label" for="input-search">{{ entry_search }}</label>
<div class="row">
<div class="col-sm-4">
<input type="text" name="search" value="{{ search }}" placeholder="{{ text_keyword }}" id="input-search" class="form-control" />
<p>{{ text_search_hint }}</p>
</div>
<div class="col-sm-3">
<select name="category_id" class="form-control">
<option value="0">{{ text_category }}</option>
{% for category_1 in categories %}
{% if category_1.category_id == category_id %}
<option value="{{ category_1.category_id }}" selected="selected">{{ category_1.name }}</option>
{% else %}
<option value="{{ category_1.category_id }}">{{ category_1.name }}</option>
{% endif %}
{% for category_2 in category_1.children %}
{% if category_2.category_id == category_id %}
<option value="{{ category_2.category_id }}" selected="selected">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ category_2.name }}</option>
{% else %}
<option value="{{ category_2.category_id }}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ category_2.name }}</option>
{% endif %}
{% for category_3 in category_2.children %}
{% if category_3.category_id == category_id %}
<option value="{{ category_3.category_id }}" selected="selected">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ category_3.name }}</option>
{% else %}
<option value="{{ category_3.category_id }}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ category_3.name }}</option>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
</select>
<form class="search-page__form" data-search-url="{{ search_url }}" onsubmit="return submitProductSearch(this);">
<label class="visually-hidden" for="search-page-input">{{ entry_search }}</label>
<input id="search-page-input" type="search" name="search" value="{{ search }}" placeholder="{{ text_keyword }}" autocomplete="off">
<button type="submit" aria-label="{{ button_search }}">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<circle cx="11" cy="11" r="6.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M16 16L21 21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>{{ button_search }}</span>
</button>
</form>
</div>
<div class="col-sm-3">
<label class="checkbox-inline">
{% if sub_category %}
<input type="checkbox" name="sub_category" value="1" checked="checked" />
{% else %}
<input type="checkbox" name="sub_category" value="1" />
{% endif %}
{{ text_sub_category }}</label>
</div>
</div>
<p>
<label class="checkbox-inline">
{% if description %}
<input type="checkbox" name="description" value="1" id="description" checked="checked" />
{% else %}
<input type="checkbox" name="description" value="1" id="description" />
{% endif %}
{{ entry_description }}</label>
</p>
<input type="button" value="{{ button_search }}" id="button-search" class="btn btn-primary" />
<h2>{{ text_search }}</h2>
{% if products %}
<div class="row">
<div class="col-md-2 col-sm-6 hidden-xs">
<div class="btn-group btn-group-sm">
<button type="button" id="list-view" class="btn btn-default" data-toggle="tooltip" title="{{ button_list }}"><i class="fa fa-th-list"></i></button>
<button type="button" id="grid-view" class="btn btn-default" data-toggle="tooltip" title="{{ button_grid }}"><i class="fa fa-th"></i></button>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="form-group">
<a href="{{ compare }}" id="compare-total" class="btn btn-link">{{ text_compare }}</a>
</div>
</div>
<div class="col-md-4 col-xs-6">
<div class="form-group input-group input-group-sm">
<label class="input-group-addon" for="input-sort">{{ text_sort }}</label>
<select id="input-sort" class="form-control" onchange="location = this.value;">
{% for sorts in sorts %}
{% if sorts.value == '%s-%s'|format(sort, order) %}
<option value="{{ sorts.href }}" selected="selected">{{ sorts.text }}</option>
{% else %}
<option value="{{ sorts.href }}">{{ sorts.text }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3 col-xs-6">
<div class="form-group input-group input-group-sm">
<label class="input-group-addon" for="input-limit">{{ text_limit }}</label>
<select id="input-limit" class="form-control" onchange="location = this.value;">
{% for limits in limits %}
{% if limits.value == limit %}
<option value="{{ limits.href }}" selected="selected">{{ limits.text }}</option>
{% else %}
<option value="{{ limits.href }}">{{ limits.text }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
</div>
<div class="row">
{% for product in products %}
<div class="product-layout product-list col-xs-12">
<div class="product-thumb">
<div class="image"><a href="{{ product.href }}"><img src="{{ product.thumb }}" alt="{{ product.name }}" title="{{ product.name }}" class="img-responsive" /></a></div>
</section>
<section class="search-page__results">
<div class="container-fluid">
{% if search %}
<div class="search-page__summary">
<div>
<div class="caption">
<h4><a href="{{ product.href }}">{{ product.name }}</a></h4>
<p>{{ product.description }}</p>
{% if product.price %}
<p class="price">
{% if not product.special %}
{{ product.price }}
{% else %}
<span class="price-new">{{ product.special }}</span> <span class="price-old">{{ product.price }}</span>
{% endif %}
{% if product.tax %}
<span class="price-tax">{{ text_tax }} {{ product.tax }}</span>
{% endif %}
</p>
{% endif %}
{% if product.rating %}
<div class="rating">
{% for i in 1..5 %}
{% if product.rating < i %}
<span class="fa fa-stack"><i class="fa fa-star-o fa-stack-2x"></i></span>
{% else %}
<span class="fa fa-stack"><i class="fa fa-star fa-stack-2x"></i><i class="fa fa-star-o fa-stack-2x"></i></span>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="button-group">
<button type="button" onclick="cart.add('{{ product.product_id }}', '{{ product.minimum }}');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md">{{ button_cart }}</span></button>
<button type="button" data-toggle="tooltip" title="{{ button_wishlist }}" onclick="wishlist.add('{{ product.product_id }}');"><i class="fa fa-heart"></i></button>
<button type="button" data-toggle="tooltip" title="{{ button_compare }}" onclick="compare.add('{{ product.product_id }}');"><i class="fa fa-exchange"></i></button>
</div>
<span class="search-page__eyebrow">{{ text_search_results }}</span>
<h2>{{ text_search_for|format(search) }}</h2>
</div>
<div class="search-page__count">{{ text_found|format(product_total) }}</div>
</div>
{% if products %}
<div class="row g-3 product-items">
{% for product in products %}
<div class="col-6 col-lg-4">
{% include 'dominik/template/common/product.twig' %}
</div>
{% endfor %}
</div>
<div class="row">
<div class="col-sm-6 text-left">{{ pagination }}</div>
<div class="col-sm-6 text-right">{{ results }}</div>
<div class="search-page__pagination">
<div class="pagination">{{ pagination }}</div>
<div>{{ results }}</div>
</div>
{% else %}
<p>{{ text_empty }}</p>
<div class="search-page__empty">
<h2>{{ text_empty }}</h2>
<p>{{ text_empty_hint }}</p>
</div>
{% endif %}
{{ content_bottom }}</div>
{{ column_right }}</div>
</div>
<script type="text/javascript"><!--
$('#button-search').bind('click', function() {
url = 'index.php?route=product/search';
var search = $('#content input[name=\'search\']').prop('value');
if (search) {
url += '&search=' + encodeURIComponent(search);
}
var category_id = $('#content select[name=\'category_id\']').prop('value');
if (category_id > 0) {
url += '&category_id=' + encodeURIComponent(category_id);
}
var sub_category = $('#content input[name=\'sub_category\']:checked').prop('value');
if (sub_category) {
url += '&sub_category=true';
}
var filter_description = $('#content input[name=\'description\']:checked').prop('value');
if (filter_description) {
url += '&description=true';
}
location = url;
});
$('#content input[name=\'search\']').bind('keydown', function(e) {
if (e.keyCode == 13) {
$('#button-search').trigger('click');
}
});
$('select[name=\'category_id\']').on('change', function() {
if (this.value == '0') {
$('input[name=\'sub_category\']').prop('disabled', true);
} else {
$('input[name=\'sub_category\']').prop('disabled', false);
}
});
$('select[name=\'category_id\']').trigger('change');
--></script>
{% else %}
<div class="search-page__empty">
<h2>{{ text_start_search }}</h2>
<p>{{ text_search_note }}</p>
</div>
{% endif %}
</div>
</section>
</main>
{{ content_bottom }}
{{ footer }}