Opencart. Товары с ценой «0» в конце списка + BrainyFilter — цена 0 рублей (или нулевая цена любой другой валюты) для товара в магазине на базе Opencart (OcStore) может использоваться с разной целью. Например, цена на товар меняется очень быстро и в некоторых случаях 0 в цене меняют на фразу «По запросу». А иногда нулевую цену используют как условия для запрета покупки данной вещи, оставляя при этом товар в каталоге для расширения ассортимента и улучшения по SEO. Примеров можно привести достаточно много и каждый использует нулевую цену по-разному.

Однако, товар с ценой «0» в каталоге при сортировке цен «по возрастанию» занимает первые места каталога, а иногда и вовсе портит картину сайта перемешиваясь с остальным товаром, сбивая с толку и нередко раздражая посетителей Вашего сайта. Исправить это можно достаточно быстро.
Изменения вносятся только в 1 файл, а именно в модель продукта, которая находится в директории:
catalog/model/catalog/product.php
В OcStore 2.1 примерно строка 160 метод getProducts(), OcStore 2.3 примерно строка 169 метод getProducts()
Находим фрагмент кода:
if (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') {
$sql .= " ORDER BY (CASE WHEN special IS NOT NULL THEN special WHEN discount IS NOT NULL THEN discount ELSE p.price END)";
} else {
$sql .= " ORDER BY " . $data['sort'];
}
} else {
$sql .= " ORDER BY p.sort_order";
}
и меняем его на следующий фрагмент:
if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
if ($data['sort'] == 'pd.name' || $data['sort'] == 'p.model') {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), LCASE(" . $data['sort'] . ")";
} elseif ($data['sort'] == 'p.price') {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), (CASE WHEN special IS NOT NULL THEN special WHEN discount IS NOT NULL THEN discount ELSE p.price END)";
} else {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), " . $data['sort'];
}
} else {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), p.sort_order";
}
Сохраняем, обновляем файл на Вашем сайте. Не забываем обновлять кеш модификаторов! Готово!
Так же можно создать модификатор, не меняя файл product.php напрямую, выглядит он следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<modification>
<name>Товар с 0 ценой в конец списка</name>
<code>last_price_null</code>
<version>1</version>
<author>valdemariuses</author>
<link>https://nikonorow.ru/</link>
<file path="catalog/model/catalog/product.php">
<operation>
<search index="0"><![CDATA[if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {]]></search>
<add position="replace" offset="10"><![CDATA[ if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
if ($data['sort'] == 'pd.name' || $data['sort'] == 'p.model') {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), LCASE(" . $data['sort'] . ")";
} elseif ($data['sort'] == 'p.price') {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), (CASE WHEN special IS NOT NULL THEN special WHEN discount IS NOT NULL THEN discount ELSE p.price END)";
} else {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), " . $data['sort'];
}
} else {
$sql .= " ORDER BY IF(p.price = '0', 1, 0), p.sort_order";
}]]></add>
</operation>
</file>
</modification>
Данный способ я обнаружил на сайте oc-new.com и благодарю автора под ником valdemariuses за решение проблемы.
Но что делать, если у Вас установлен Brainy Filter или как его еще называют — умный фильтр?
Через стандартную модель product.php и способ сортировки товаров, описанный выше, сделать не получится, поскольку у плагина умного фильтра своя сортировка и она перезаписывает стандартную функцию getProducts. Что же требуется сделать для того, чтобы убрать товар с ценой «0» в конец списка?
Находим модель плагина bf, находится он по следующему адресу:
/catalog/model/extension/module/brainyfilter.php
Ищем функцию prepareQueryForCategory(), выглядит она так:
public function prepareQueryForCategory()
{
$sql = $this->_prepareFilterQuery()->limit($this->productsLimit, $this->productsStart);
$sql->innerJoin(array('pd' => 'product_description'), 'pd.product_id = p.product_id')
->where('pd.language_id = ?', (int)$this->config->get('config_language_id'));
switch ($this->sortBy)
{
case 'pd.name' : {
$sql->order(array("LCASE(pd.name) {$this->order}"));
break;
}
case 'p.model' : {
$sql->select(array('pp.model'))->order(array("LCASE(pp.model) {$this->order}"))
->innerJoin(array('pp' => 'product'), 'pp.product_id = p.product_id');
break;
}
case 'p.quantity' : {
$sql->select(array('pp.quantity'))->order(array("pp.quantity {$this->order}", ))
->innerJoin(array('pp' => 'product'), 'pp.product_id = p.product_id');
break;
}
case 'p.price' : {
if (self::$SKIP_TMP_TABLE) {
$this->_applyPriceFilterCondition($sql);
}
$sql->order(array("actual_price {$this->order}"));
break;
}
case 'rating' : {
$sql->leftJoin(array('f' => self::TBL_FILTER), 'f.product_id = p.product_id')
->where('f.filter_group = "r0"')
->order(array("f.filter_id {$this->order}"));
break;
}
case 'p.sort_order' : {
$sql->select(array('pp.sort_order'))->order(array("pp.sort_order {$this->order}"))
->innerJoin(array('pp' => 'product'), 'pp.product_id = p.product_id');
break;
}
case 'p.date_added' : {
$sql->select(array('pp.date_added'))->order(array("pp.date_added {$this->order}"))
->innerJoin(array('pp' => 'product'), 'pp.product_id = p.product_id');
break;
}
}
if ($this->sortBy !== 'pd.name') {
$sql->order(array("pd.name {$this->order}"));
}
return (string)$sql;
}
и после фрагмента:
if ($this->sortBy !== 'pd.name') {
$sql->order(array("pd.name {$this->order}"));
}
вставляем следующий код:
if ($this->sortBy == 'p.price') {
$sql = (string)$sql;
$pos = strrpos($sql, 'ORDER BY');
if ($pos !== false) {
$sql1 = substr($sql,0,$pos);
$sql2 = str_replace(', pd.name ' . $this->order, '' ,substr($sql,$pos-1 + 9));
$sql3 = "SELECT * from (" . $sql1 . ") pt " . " ORDER BY actual_price = 0, " . $sql2;
$sql = $sql3;
}
}
Не вижу смысла перестраивать запрос, проще сделать так, пускай и код будет не самым красивым.
Аналогично с первым способом, сохраняем, обновляем файл на Вашем сайте. Не забываем обновлять кеш модификаторов! Готово! Теперь мы подружили Brainy Filter c нужной нам сортировкой нулевой цены.
UPD: Найден способ перемещением товара, которого нет в наличии в конец списка при использовании Brainy Filter. Благодарю Александра Сергеева за то, что написал мне в личные сообщения ВКонтакте. Задача была таковой, цитирую: «стоит сортировка по цене, но надо дополнительно еще было сделать вниз сортировку по количеству, чтобы нулевые вниз ушли».
Итак, приступим:
Как я писал выше, находим модель плагина bf, находится он по следующему адресу:
/catalog/model/extension/module/brainyfilter.php
Изменения в коде модели (описание). Перед строкой:
$sql->order(array("actual_price {$this->order}"));
Добавил:
$sql->order(array("p.quantity=0 {$this->order}"));
И получилось, что в конце запрос содержит:
... match_filters = 1 ORDER BY actual_price ASC, p.quantity=0 ASC LIMIT 0, 100
Изменения в коде модели (полный фрагмент после применения изменений), заменяем его в самом файле фильтра:
case 'p.price' : {
if (self::$SKIP_TMP_TABLE) {
$this->_applyPriceFilterCondition($sql);
}
$sql->order(array("p.quantity=0 {$this->order}"));
$sql->order(array("actual_price {$this->order}"));
break;
}
Протестировано на OcStore 2.3
Дата создания статьи: 05.06.2021
Рубрика: Веб-разработка
Спасибо большое, очень помогли!
Спасибо за комментарий 🙂