Рекомендуемые статьи и товары

This commit is contained in:
Konstantin
2026-05-31 11:48:41 +03:00
parent 5a677f7db7
commit 15446a6402
14 changed files with 275 additions and 50 deletions
+62 -10
View File
@@ -375,11 +375,23 @@ $data['price_n'] = $product_info['price'];
$results = $this->model_catalog_product->getProductRelated($this->request->get['product_id']);
if (!$results) {
$results = $this->model_catalog_product->getRandomProductsFromSameCategories($this->request->get['product_id'], 4);
}
foreach ($results as $result) {
if ($result['image']) {
$image = $this->model_tool_image->resize($result['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_related_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_related_height'));
$image = $this->model_tool_image->resize($result['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'));
} else {
$image = $this->model_tool_image->resize('placeholder.png', $this->config->get('theme_' . $this->config->get('config_theme') . '_image_related_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_related_height'));
$image = $this->model_tool_image->resize('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']);
if ($product_images) {
$additional_image = $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'));
} else {
$additional_image = false;
}
if ($this->customer->isLogged() || !$this->config->get('config_customer_price')) {
@@ -408,17 +420,57 @@ $data['price_n'] = $product_info['price'];
$rating = false;
}
$rent_prices = array_filter([$result['price'], $result['price_2']], function($price) {
return (float)$price > 0;
});
$min_price = $rent_prices ? min($rent_prices) : 0;
$data['products'][] = array(
'product_id' => $result['product_id'],
'product_id' => $result['product_id'],
'thumb' => $image,
'additional_thumb' => $additional_image,
'name' => $result['name'],
'description' => utf8_substr(trim(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8'))), 0, $this->config->get('theme_' . $this->config->get('config_theme') . '_product_description_length')) . '..',
'price' => $price,
'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']),
'price_3_n' => $result['price_3'],
'min_price' => $this->currency->format($this->tax->calculate($min_price, $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']),
'special' => $special,
'tax' => $tax,
'minimum' => $result['minimum'] > 0 ? $result['minimum'] : 1,
'rating' => $rating,
'href' => $this->url->link('product/product', 'product_id=' . $result['product_id'])
);
}
$this->load->model('blog/article');
$data['articles'] = array();
$results = $this->model_blog_article->getArticleRelatedByProduct(array(
'product_id' => $this->request->get['product_id'],
'limit' => 3
));
foreach ($results as $result) {
if ($result['image']) {
$image = $this->model_tool_image->resize($result['image'], $this->config->get('configblog_image_article_width'), $this->config->get('configblog_image_article_height'));
} else {
$image = $this->model_tool_image->resize('placeholder.png', $this->config->get('configblog_image_article_width'), $this->config->get('configblog_image_article_height'));
}
$data['articles'][] = array(
'article_id' => $result['article_id'],
'thumb' => $image,
'name' => $result['name'],
'description' => utf8_substr(trim(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8'))), 0, $this->config->get('theme_' . $this->config->get('config_theme') . '_product_description_length')) . '..',
'price' => $price,
'special' => $special,
'tax' => $tax,
'minimum' => $result['minimum'] > 0 ? $result['minimum'] : 1,
'rating' => $rating,
'href' => $this->url->link('product/product', 'product_id=' . $result['product_id'])
'description' => utf8_substr(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8')), 0, $this->config->get('configblog_article_description_length')) . '..',
'date_added' => date($this->language->get('date_format_short'), strtotime($result['date_added'])),
'viewed' => $result['viewed'],
'href' => $this->url->link('blog/article', 'article_id=' . $result['article_id'])
);
}
+69 -5
View File
@@ -6,15 +6,47 @@ class ControllerToolCallback extends Controller {
$json = array();
$json['error'] = [];
if($this->request->post['product_id']){
$telephone = isset($this->request->post['telephone']) && is_scalar($this->request->post['telephone']) ? trim((string)$this->request->post['telephone']) : '';
if (utf8_strlen($telephone) < 3 || utf8_strlen($telephone) > 32) {
$json['error'][] = 'Укажите номер телефона.';
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
return;
}
$data['request_type'] = isset($this->request->post['request_type']) ? $this->request->post['request_type'] : '';
$data['fitting_room_products'] = array();
if ($data['request_type'] == 'fitting_room') {
$data['fitting_room_products'] = $this->getFittingRoomProducts();
if (!$data['fitting_room_products']) {
$json['error'][] = 'Ваша примерочная пуста. Добавьте хотя бы одно платье и попробуйте снова.';
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
return;
}
}
if (isset($this->request->post['product_id']) && $this->request->post['product_id']) {
$this->load->model('catalog/product');
$data['product_info'] = $this->model_catalog_product->getProduct($this->request->post['product_id']);
}
$this->load->model('localisation/zone');
$zone_id = isset($this->session->data['city_id']) ? (int)$this->session->data['city_id'] : (int)$this->config->get('config_zone_id');
$zone = $this->model_localisation_zone->getZone($zone_id);
$data['city'] = $zone ? $zone['name'] : '';
$data['config_name'] = $this->config->get('config_name');
$data['post'] = $this->request->post;
@@ -53,6 +85,38 @@ class ControllerToolCallback extends Controller {
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}
private function getFittingRoomProducts() {
$product_ids = array();
if ($this->customer->isLogged()) {
$this->load->model('account/wishlist');
foreach ($this->model_account_wishlist->getWishlist() as $result) {
$product_ids[] = (int)$result['product_id'];
}
} elseif (isset($this->session->data['wishlist']) && is_array($this->session->data['wishlist'])) {
$product_ids = array_map('intval', $this->session->data['wishlist']);
}
$this->load->model('catalog/product');
$products = array();
foreach (array_unique($product_ids) as $product_id) {
$product_info = $this->model_catalog_product->getProduct($product_id);
if ($product_info) {
$products[] = array(
'name' => $product_info['name'],
'model' => $product_info['model'],
'href' => $this->url->link('product/product', 'product_id=' . (int)$product_id)
);
}
}
return $products;
}
private function sendTG($message){
@@ -91,4 +155,4 @@ class ControllerToolCallback extends Controller {
return $response;
}
}
}
@@ -15,7 +15,7 @@ $_['text_edit'] = 'Изменить контактную информа
$_['text_password'] = 'Изменить свой пароль';
$_['text_address'] = 'Изменить мои адреса';
$_['text_credit_card'] = 'Управление сохраненными кредитными картами';
$_['text_wishlist'] = 'Изменить закладки';
$_['text_wishlist'] = 'Виртуальная примерочная';
$_['text_order'] = 'История заказов';
$_['text_download'] = 'Файлы для скачивания';
$_['text_reward'] = 'Бонусные баллы';
@@ -26,4 +26,4 @@ $_['text_recurring'] = 'Регулярные платежи';
$_['text_transactions'] = 'Операции';
$_['text_affiliate_add'] = 'Зарегистрировать партнерский аккаунт';
$_['text_affiliate_edit'] = 'Изменить партнерскую информацию';
$_['text_tracking'] = 'Код отслеживания партнеров';
$_['text_tracking'] = 'Код отслеживания партнеров';
@@ -10,7 +10,7 @@ $_['text_account'] = 'Аккаунт';
$_['text_login'] = 'Авторизация';
$_['text_new_customer'] = 'Новый покупатель';
$_['text_register'] = 'Регистрация';
$_['text_register_account'] = 'Создание учётной записи поможет делать покупки быстрее и удобнее. Вы также сможете отслеживать статус своего заказа, пользоваться закладками, видеть свои предыдущие заказы или получить скидку как наш постоянный покупатель.';
$_['text_register_account'] = 'Создание учётной записи поможет делать покупки быстрее и удобнее. Вы также сможете отслеживать статус своего заказа, пользоваться виртуальной примерочной, видеть свои предыдущие заказы или получить скидку как наш постоянный покупатель.';
$_['text_returning_customer'] = 'Постоянный покупатель';
$_['text_i_am_returning_customer'] = 'Я совершал здесь покупки ранее и регистрировался';
$_['text_forgotten'] = 'Забыли пароль?';
@@ -22,4 +22,4 @@ $_['entry_password'] = 'Пароль';
// Error
$_['error_login'] = 'Не найден введённый адрес E-Mail и/или пароль указан неправильно.';
$_['error_attempts'] = 'Ваша учетная запись превысила допустимое количество попыток входа в систему. Пожалуйста, попробуйте еще через 1 час.';
$_['error_approved'] = 'Вы сможете войти после проверки учётной записи администрацией магазина.';
$_['error_approved'] = 'Вы сможете войти после проверки учётной записи администрацией магазина.';
@@ -19,6 +19,6 @@ $_['text_bestseller'] = 'Хиты Продаж';
$_['text_mostviewed'] = 'Популярные Товары';
$_['text_account'] = 'Личный кабинет';
$_['text_order'] = 'История заказов';
$_['text_wishlist'] = 'Закладки';
$_['text_wishlist'] = 'Виртуальная примерочная';
$_['text_newsletter'] = 'Рассылка';
$_['text_powered'] = 'Работает на <a target="_blank" href="https://ocstore.com/?utm_source=ocstore3_install">ocStore</a><br /> %s &copy; %s';
@@ -4,7 +4,7 @@
// Text
$_['text_home'] = 'Главная';
$_['text_wishlist'] = 'Закладки (%s)';
$_['text_wishlist'] = 'Примерочная (%s)';
$_['text_shopping_cart'] = 'Корзина';
$_['text_category'] = 'Категории';
$_['text_account'] = 'Личный кабинет';
@@ -15,4 +15,4 @@ $_['text_transaction'] = 'Операции';
$_['text_download'] = 'Файлы для скачивания';
$_['text_logout'] = 'Выход';
$_['text_checkout'] = 'Оформление заказа';
$_['text_search'] = 'Поиск';
$_['text_search'] = 'Поиск';
@@ -14,11 +14,11 @@ $_['text_account'] = 'Моя информация';
$_['text_edit'] = 'Изменить контактную информацию';
$_['text_password'] = 'Пароль';
$_['text_address'] = 'Список контактов';
$_['text_wishlist'] = 'Закладки';
$_['text_wishlist'] = 'Виртуальная примерочная';
$_['text_order'] = 'История заказов';
$_['text_download'] = 'Файлы для скачивания';
$_['text_reward'] = 'Бонусные баллы';
$_['text_return'] = 'Возвраты';
$_['text_transaction'] = 'Платежи';
$_['text_newsletter'] = 'E-Mail рассылка';
$_['text_recurring'] = 'Регулярные платежи';
$_['text_recurring'] = 'Регулярные платежи';
@@ -21,7 +21,8 @@ $_['text_login'] = 'Пожалуйста <a href="%s">автори
$_['text_no_reviews'] = 'Нет отзывов об этом товаре.';
$_['text_note'] = '<span style="color: #FF0000;">Примечание:</span> HTML разметка не поддерживается! Используйте обычный текст.';
$_['text_success'] = 'Спасибо за ваш отзыв. Он поступил администратору для проверки на спам и вскоре будет опубликован.';
$_['text_related'] = 'Рекомендуемые товары';
$_['text_related'] = 'Посмотрите другие предложения';
$_['text_related_article'] = 'Статьи по теме';
$_['text_tags'] = 'Теги:';
$_['text_error'] = 'Товар не найден!';
$_['text_payment_recurring'] = 'Платежный профиль';
@@ -34,6 +35,7 @@ $_['text_semi_month'] = 'Полмесяца';
$_['text_month'] = 'Месяц';
$_['text_year'] = 'Год';
$_['text_benefits'] = 'Преимущества:';
$_['button_more'] = 'Подробнее';
// Entry
$_['entry_qty'] = 'Кол-во';
@@ -51,4 +53,4 @@ $_['tab_review'] = 'Отзывов (%s)';
// Error
$_['error_name'] = 'Имя должно содержать от 3 до 25 символов!';
$_['error_text'] = 'Текст отзыва должен содержать от 25 до 1000 символов!';
$_['error_rating'] = 'Пожалуйста, выберите оценку!';
$_['error_rating'] = 'Пожалуйста, выберите оценку!';
+1 -1
View File
@@ -29,7 +29,7 @@ $_['button_continue'] = 'Продолжить';
$_['button_cart'] = 'Купить';
$_['button_cancel'] = 'Отмена';
$_['button_compare'] = 'В сравнение';
$_['button_wishlist'] = 'В закладки';
$_['button_wishlist'] = 'В примерочную';
$_['button_checkout'] = 'Оформление заказа';
$_['button_confirm'] = 'Подтверждение заказа';
$_['button_coupon'] = 'Применение купона';
+6 -2
View File
@@ -245,12 +245,16 @@ class ModelBlogArticle extends Model {
$this->load->model('blog/article');
$sql = "SELECT * FROM " . DB_PREFIX . "product_related_article np LEFT JOIN " . DB_PREFIX . "article p ON (np.article_id = p.article_id) LEFT JOIN " . DB_PREFIX . "article_to_store p2s ON (p.article_id = p2s.article_id) WHERE np.product_id = '" . (int)$data['product_id'] . "' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' LIMIT " . (int)$data['limit'];
$sql = "SELECT DISTINCT p.article_id FROM ((SELECT article_id FROM " . DB_PREFIX . "product_related_article WHERE product_id = '" . (int)$data['product_id'] . "') UNION (SELECT article_id FROM " . DB_PREFIX . "article_related_product WHERE product_id = '" . (int)$data['product_id'] . "')) related LEFT JOIN " . DB_PREFIX . "article p ON (related.article_id = p.article_id) LEFT JOIN " . DB_PREFIX . "article_to_store p2s ON (p.article_id = p2s.article_id) WHERE p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "' LIMIT " . (int)$data['limit'];
$query = $this->db->query($sql);
foreach ($query->rows as $result) {
$article_data[$result['article_id']] = $this->model_blog_article->getArticle($result['article_id']);
$article = $this->model_blog_article->getArticle($result['article_id']);
if ($article) {
$article_data[$result['article_id']] = $article;
}
}
return $article_data;
+29
View File
@@ -398,6 +398,35 @@ class ModelCatalogProduct extends Model
return $product_data;
}
public function getRandomProductsFromSameCategories($product_id, $limit = 4)
{
$product_data = array();
$cache = 'product.related.random.' . (int) $this->config->get('config_store_id') . '.' . (int) $product_id . '.' . (int) $limit;
$product_ids = $this->cache->get($cache);
if (!$product_ids) {
$product_ids = array();
$query = $this->db->query('SELECT DISTINCT p.product_id FROM ' . DB_PREFIX . 'product_to_category source_p2c INNER JOIN ' . DB_PREFIX . 'product_to_category related_p2c ON (source_p2c.category_id = related_p2c.category_id) INNER JOIN ' . DB_PREFIX . 'product p ON (related_p2c.product_id = p.product_id) INNER JOIN ' . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE source_p2c.product_id = '" . (int) $product_id . "' AND p.product_id != '" . (int) $product_id . "' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int) $this->config->get('config_store_id') . "' ORDER BY RAND() LIMIT " . (int) $limit);
foreach ($query->rows as $result) {
$product_ids[] = $result['product_id'];
}
$this->cache->set($cache, $product_ids);
}
foreach ($product_ids as $related_id) {
$product = $this->getProduct($related_id);
if ($product) {
$product_data[$related_id] = $product;
}
}
return $product_data;
}
public function getProductLayoutId($product_id)
{
$query = $this->db->query('SELECT * FROM ' . DB_PREFIX . "product_to_layout WHERE product_id = '" . (int) $product_id . "' AND store_id = '" . (int) $this->config->get('config_store_id') . "'");
@@ -516,23 +516,27 @@ function citySelect(el){
});
}
function SendRequest(product_id = 0){
var params = {};
params['title'] = product_id ? 'Оставьте заявку' : 'Отправить сообщение';
params['width'] = 420;
var form = $("<form>");
form.append($(product_id ? '<p>И мы свяжемся с вами, чтобы назначить время примерки платья</p>' : '<p>Оставьте своё сообщение и мы свяжемся с Вами для консультации</p>').addClass('text-center'));
if(product_id){
form.append($("<div>").text($(".product-title").text()).addClass('text-center heading mb-4'))
function SendRequest(product_id = 0, request_type = ''){
var params = {};
var isFittingRoom = request_type == 'fitting_room';
params['title'] = isFittingRoom ? 'Записаться на примерку' : (product_id ? 'Оставьте заявку' : 'Отправить сообщение');
params['width'] = 420;
var form = $("<form>");
form.append($(isFittingRoom ? '<p>Оставьте номер телефона. Мы получим список выбранных платьев и свяжемся с вами, чтобы назначить время примерки.</p>' : (product_id ? '<p>И мы свяжемся с вами, чтобы назначить время примерки платья</p>' : '<p>Оставьте своё сообщение и мы свяжемся с Вами для консультации</p>')).addClass('text-center'));
if(isFittingRoom){
form.append('<input type="hidden" name="request_type" value="fitting_room">');
}
if(product_id){
form.append($("<div>").text($(".product-title").text()).addClass('text-center heading mb-4'))
form.append('<input type="hidden" name="product_id" value="'+product_id+'">');
}
form.append('<div class="form-wrap mb-3"><label>Ваше имя</label><input type="text" class="form-control" name="name"></div>');
form.append('<div class="form-wrap mb-3"><label>Номер телефона</label><input type="tel" class="form-control" name="telephone" required ></div>');
form.append('<div class="form-wrap mb-3"><label>Комментарий</label><textarea rows="2" class="form-control" name="comment"></textarea></div>');
form.append('<button type="submit" class="btn btn-dark w-100 d-block">ЗАПИСАТЬСЯ на ПРИМЕРКУ</button>')
form.append('<button type="submit" class="btn btn-dark w-100 d-block">'+(isFittingRoom ? 'ОТПРАВИТЬ ЗАЯВКУ' : 'ЗАПИСАТЬСЯ на ПРИМЕРКУ')+'</button>')
params['modal'] = form;
@@ -1,12 +1,22 @@
<h3>{{ config_name }}</h3>
<hr>
{% if product_info %}
<div><b>Платье:</b> {{ product_info.name }}</div>
<div><b>Код товара:</b> {{ product_info.model }}</div>
{% endif %}
{% if post.name %}
<div><b>Имя:</b> {{ post.name }}</div>
{% endif %}
<h3>{{ config_name }}</h3>
<hr>
{% if request_type == 'fitting_room' %}
<div><b>Заявка:</b> запись из виртуальной примерочной</div>
<div><b>Платья в примерочной на момент заявки:</b></div>
{% for product in fitting_room_products %}
<div>{{ loop.index }}. <a href="{{ product.href }}">{{ product.name }}</a> (код: {{ product.model }})</div>
{% endfor %}
{% endif %}
{% if product_info %}
<div><b>Платье:</b> {{ product_info.name }}</div>
<div><b>Код товара:</b> {{ product_info.model }}</div>
{% endif %}
{% if city %}
<div><b>Город:</b> {{ city }}</div>
{% endif %}
{% if post.name %}
<div><b>Имя:</b> {{ post.name }}</div>
{% endif %}
{% if post.telephone %}
<div><b>Телефон:</b> {{ post.telephone }}</div>
{% endif %}
@@ -14,4 +24,4 @@
<div><b>Комментарий:</b><br> {{ post.comment|nl2br }}</div>
{% endif %}
<hr>
<div>{{ ''|date('d.m.Y H:i:s') }}</div>
<div>{{ ''|date('d.m.Y H:i:s') }}</div>
@@ -188,6 +188,66 @@
</div>
</div>
</section>
{% if products %}
<section class="blog-article-section blog-article-products">
<div class="container-fluid">
<div class="blog-article-section__head">
<span class="blog-category-eyebrow">Вам может понравиться</span>
<h2>{{ text_related }}</h2>
</div>
<div class="row g-3 product-items">
{% for product in products %}
<div class="col-6 col-lg-3">
{% include 'dominik/template/common/product.twig' %}
</div>
{% endfor %}
</div>
</div>
</section>
{% endif %}
{% if articles %}
<section class="blog-article-section blog-article-related">
<div class="container-fluid">
<div class="blog-article-section__head">
<span class="blog-category-eyebrow">Полезно знать</span>
<h2>{{ text_related_article }}</h2>
</div>
<div class="blog-article-related__grid">
{% for article in articles %}
<article class="blog-category-card">
<a href="{{ article.href }}" class="blog-category-card__image">
<img src="{{ article.thumb }}" alt="{{ article.name }}" title="{{ article.name }}" loading="lazy" />
</a>
<div class="blog-category-card__body">
<div class="blog-category-card__meta">
<time>{{ article.date_added }}</time>
<span class="blog-category-card__divider"></span>
<span class="blog-category-card__views">
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
<path d="M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z"></path>
<circle cx="12" cy="12" r="2.5"></circle>
</svg>
{{ article.viewed }}
</span>
</div>
<h3><a href="{{ article.href }}">{{ article.name }}</a></h3>
<p>{{ article.description }}</p>
<div class="blog-category-card__footer">
<a href="{{ article.href }}" class="blog-category-card__link">
{{ button_more }}
<svg width="18" height="12" viewBox="0 0 18 12" aria-hidden="true">
<path d="M0 6h16M11 1l5 5-5 5"></path>
</svg>
</a>
</div>
</div>
</article>
{% endfor %}
</div>
</div>
</section>
{% endif %}
{{ content_bottom }}
{{ footer }}
{#