Opencart, OcStore: Умный фильтр и поиск
Запуская интернет магазин, столкнулся с проблемой при которой фильтр товаров не отрабатывал на подстроку, которая начинается не с первого символа. Исправил в своей версии, получилось удобно. Решил запилить про это пост-заметку, чтобы в будущем использовать и в других местах..
Итак, начнем с описания проблемы:
- Заходим в админку и создаем товар, с названием Test12345
- Пробуем вбить в фильтр в поле имени: Test — видим наш товар
- Пробуем вбить в фильтр в поле имени: t123 — не видим наш товар
Смотрим исходник контроллера: /admin/controller/catalog/product.php
там видим, что запрос передается в модель: /admin/model/catalog/product.php
открываем её и видим что запрос формируется таким образом:
Как видим фильтр позволяет опускать только последнюю часть. Сделанно так неспроста и связанно это с тем как mysql обрабатывает запросы, а точнее с производительностью. Вдаваться в подробности не буду, пост не про это. Кому интересно читайте в справке mysql о том как работает LIKE оператор.
Что же делать, ведь всё же хочется иногда искать по выражению %Condition% — все просто, добавим условный оператор в запрос, при наличии которого будет искать по %Condition%, а во всех остальных случаях, по оригинальному Condition%. В качестве такого оператора, я выбрал знак восклицания. Меняем код так :
OpenCart: как добавить фильтр по артикулу в админке
В стандартном фильтре в админке нельзя «фильтровать» товары по артикулу. Когда у вас тысячи товаров, такой фильтр в разы ускоряет поиск конкретного товара.
Что делать
protected function getList()
if (isset($this->request->get['filter_sku'])) < $filter_sku = $this->request->get['filter_sku']; > else < $filter_sku = ''; >$data['filter_sku'] = $filter_sku;
if (isset($this->request->get['filter_sku'])) < $url .= '&filter_sku=' . $this->request->get['filter_sku']; >
'filter_sku' => isset($filter_sku) ? $filter_sku : '',
- Сохраняем, обновляеем кэш.
- Открываем сайт/admin/model/catalog/product.php
- Находим код (~361 строка):
if (!empty($data['filter_name'])) < $sql .= " AND pd.name LIKE '" . $this->db->escape($data['filter_name']) . "%'"; >
if (isset($this->request->get['filter_sku']) && !empty($data['filter_sku'])) < $sql .= " AND p.sku = '" . $this->db->escape($data['filter_sku']) . "'"; >
- Сохраняем, обновляем кэш.
- Открываем сайт/admin/view/template/catalog/product_list.twig
- Находим код (~68 строка):
var filter_status = $('select[name=\'filter_status\']').val();
var filter_sku = $('input[name=\'filter_sku\']').val(); if (filter_sku)
Стикеры для товаров: новинка, рекомендуемый, популярный
Здесь расскажу как добавить выбор дополнительного статуса (стикера) для товара в админке (в списке и в карточке) и вывести этот статус на сайт. Понадобится добавить одно поле в базу данных (БД), внести правки в файлы. Способ, который здесь описан, вероятно самый легкий с точки зрения нагрузки, но всего возможно не более 9 вариантов стикеров (в большинстве случаев их и столько не нужно).
Для начала в админке сделаю вывод в форму и список товаров, с возможностью сортировки, выведу на сайт. Затем сделаю управление не только через форму товара, но и через checkbox-ы в списке товаров.
Статусы (стикеры) которые буду добавлять:
Позже сможете свой список сделать, не сложно.
Шаг 1. БД
Первое что нужно сделать - добавить поле в БД. Начинаем с этого, т.к. если поля не будет к моменту обращения к нему, будет ошибка. Поле нужно только одно, вариантов статуса может быть сколько угодно, отличаться будут цифрой. Соответственно, добавляем в таблицу oc_product (вместо oc_ может быть любой другой префикс) следующее поле:
- Имя: sticker
- Тип: INT
- Длина: 11
- По умолчанию: 0 (как определено) - для всех "обычных" товаров
Поле добавлено, в БД больше ничего не нужно, идем дальше. Теперь выводим поле в админке + предусматриваем, что будет сортировка и фильтрация по этому полю (правки model - controller - view). Ничего сложного, но долго и скучно (интереснее будет позже), можно копировать существуюшие части кода и менять на свое. Начнем.
Шаг 2. Admin. Model.
admin/model/catalog/product.php
Здесь ищем по "status", почти везде где находим, добавляем аналогично "sticker":
, status = '" . (int)$data['status'] . "', sticker = '" . (int)implode('', $data['sticker']) . "'
Здесь поясню. Из формы статусы приходят в виде массива, а в базу нужно записать число, соответственно преобразуем и записываем. Затем, при выводе, нужно будет это число "расчленить" на цифры и преобразовать в массив.
Идем дальше и находим (так же 2 раза):
if (isset($data['filter_status']) && !is_null($data['filter_status']))
Ниже добавляем (здесь есть небольшое отличие):
if (isset($data['filter_sticker']) && !is_null($data['filter_sticker']))
Это нужно для сортировки по стикеру. Т.к. в поле несколько цифр, добавлен "%"
$sort_data = array( 'pd.name', 'p.model', 'p.price', 'p.quantity', 'p.status', 'p.sort_order' );
И внутрь добавляем (для сортировки, хотя польза от нее в данном случае сомнительна):
Здесь пока все. Переходим в контроллер и действуем по тому же принципу.
Шаг 3. Admin. Controller.
admin/controller/catalog/product.php
Находим (таких будет 8, не 4):
if (isset($this->request->get['filter_status'])) < $url .= '&filter_status=' . $this->request->get['filter_status']; >
if (isset($this->request->get['filter_sticker'])) < $url .= '&filter_sticker=' . $this->request->get['filter_sticker']; >
if (isset($this->request->get['filter_status'])) < $filter_status = $this->request->get['filter_status']; > else
if (isset($this->request->get['filter_sticker'])) < $filter_sticker = $this->request->get['filter_sticker']; > else
'filter_status' => $filter_status,
'filter_sticker' => $filter_sticker,
В массив $data['products'][] добавляем (строка 390-какаято, это вывод отдельного товара в таблицу):
'sticker' => str_split($result['sticker']),
$data['sort_status'] = $this->url->link('catalog/product', 'token=' . $this->session->data['token'] . '&sort=p.status' . $url, true);
$data['sort_sticker'] = $this->url->link('catalog/product', 'token=' . $this->session->data['token'] . '&sort=p.sticker' . $url, true);
$data['filter_status'] = $filter_status;
$data['filter_sticker'] = $filter_sticker;
Ранее были беспорядочные правки, в основном для списка и формы сразу (одни и те же замены), следующее выводит поле в форму. Находим:
if (isset($this->request->post['status'])) < $data['status'] = $this->request->post['status']; > elseif (!empty($product_info)) < $data['status'] = $product_info['status']; >else
if (isset($this->request->post['sticker'])) < $data['sticker'] = $this->request->post['sticker']; > elseif (!empty($product_info)) < $data['sticker'] = str_split($product_info['sticker']);//число в массив >else
Шаг 4. Admin. View.
Состоит из двух файлов шаблонов, один отвечает за форму, другой за список. Начнем с формы.
admin/view/template/catalog/product_form.tpl
, ,
Здесь уже можно зайти в форму товара и проверить. Теперь список.
admin/view/template/catalog/product_list.tpl
1) < echo '
'; > ?> Новый else if ($sticker == 2) < ?>Популярный else if ($sticker == 3) < ?>Рекомендуем ?> ?>
var filter_status = $('select[name=\'filter_status\']').val(); if (filter_status != '*')
var filter_sticker = $('select[name=\'filter_sticker\']').val(); if (filter_sticker != '*')
На этом в админке добавлено, далее вывод на сайт
Шаг 5. Frontend.
Здесь изменений меньше, что касается количества кода, поэтому не буду разбивать на отдельные шаги, все в одном, по файлам.
1. Catalog. Model. - catalog/model/catalog/product.php
В самом начале, где выводятся все поля товара, добавим sticker (здесь покажу только часть кода, нет смысла копировать все):
'minimum' => $query->row['minimum'], 'sort_order' => $query->row['sort_order'], 'status' => $query->row['status'], 'date_added' => $query->row['date_added'],
Здесь, например после status:
'sticker' => $query->row['sticker'] ? str_split($query->row['sticker']) : array(),
Здесь добавил проверку, что бы исключить "0" и сразу преобразовал в массив. И идем дальше, к контроллеру
2. Catalog. Controller. Здесь будет два примера - для контроллера отдельного товара и для контроллеров со списком товаров, вторых много, но все аналогично.
2.1. Отдельный товар. catalog/controller/product/product.php
$data['model'] = $product_info['model'];
Добавим (всего одна строка, остальное в шаблоне):
$data['sticker'] = $product_info['sticker'];
2.2. Список товаров. На примере категории, аналогично в модулях, рекомендуемые в контроллере товара, поиск и т.д. catalog/controller/product/category.php
Внутри этого массива добавим (например, после 'rating' => $result['rating'],):
Далее вывод в шаблон, опять же, для двух вариантов
3.1. Отдельный товар. catalog/view/theme/default/template/product/product.tpl
else if ($item == 2) < $name = 'Популярный'; >else if ($item == 3) < $name = 'Рекомендуем'; >?> ?> ?>
$name = 'NONAME' - добавлено на всякий случай. Если вдруг добавили новый вариант стикера и не везде прописали.
3.2. Шаблон списка товаров. catalog/view/theme/default/template/product/category.tpl
else if ($item == 2) < $name = 'Популярный'; >else if ($item == 3) < $name = 'Рекомендуем'; >?> ?> ?>
Осталось скопировать последнее в шаблоны и контроллеры модулей для вывода товара, в поиск, товары производителя, акции и готово. Для большего удобства, что бы не заходить каждый раз в форму товара, лучше сделать назначение стикера из списка товаров в админке.
Шаг 6. Admin. Смена стикера в списке товаров (Ajax).
Снова понадобятся правки в модели, контроллере и шаблоне списка товаров в админке. Для начала - пропишем в шаблон выбор стикера, затем функцию в контроллер и еще одну в модель. Работаем с тем, что получилось после предыдущих изменений (которые кстати немного переписал для удобства).
1. admin/view/template/catalog/product_list.tpl - здесь находим в таблице вывод текущего стикера. Вот этот код:
1) < echo '
'; > ?> Новый else if ($sticker == 2) < ?>Популярный else if ($sticker == 3) < ?>Рекомендуем ?> ?>
Его нужно изменить. Будет список checkbox, как и в форме. Меняем:
?> /> Новый
?> /> Популярный
?> /> Рекомендуем
Атрибут data-id у td нужен что бы узнать для какого товара делать изменение. Теперь напишем ajax запрос к функции в контроллере (которую чуть позже добавим). Перед :
Теперь нужно добавить функцию changeSticker в контроллер
2. admin/controller/catalog/product.php здесь находим:
public function autocomplete()
Перед этой добавляем нашу функцию
public function changeSticker() < $this->load->model('catalog/product'); $output=''; if(isset($this->request->get['product_id']) && isset($this->request->get['sticker']))< $product_id = (int)$this->request->get['product_id']; $sticker = (int)$this->request->get['sticker']; $output = $this->model_catalog_product->changeSticker($product_id, $sticker); > $this->response->setOutput($output); >
Из контроллера есть обращение к функции в модели. Значит теперь нужно прописать функцию и туда. последний шаг, на котором запишем новый стикер в БД
3. admin/model/catalog/product.php здесь находим:
public function getTotalProductsByLayoutId($layout_id)
Перед найденной добавляем:
public function changeSticker($product_id, $sticker)< if ($this->user->hasPermission('modify', 'catalog/product')) < $this->db->query("UPDATE " . DB_PREFIX . "product SET sticker = '" . (int)$sticker . "' WHERE product_id = '" . (int)$product_id . "'"); return 'ok'; > >
На этом все. Ocmod будет (уже жалею, что сразу не сделал), но позже