X

Opencart, OcStore: Умный фильтр и поиск

Запуская интернет магазин, столкнулся с проблемой при которой фильтр товаров не отрабатывал на подстроку, которая начинается не с первого символа. Исправил в своей версии, получилось удобно. Решил запилить про это пост-заметку, чтобы в будущем использовать и в других местах..

Итак, начнем с описания проблемы:

  1. Заходим в админку и создаем товар, с названием Test12345
  2. Пробуем вбить в фильтр в поле имени: Test - видим наш товар
  3. Пробуем вбить в фильтр в поле имени: t123 - не видим наш товар

Смотрим исходник контроллера: /admin/controller/catalog/product.php

там видим, что запрос передается в модель: /admin/model/catalog/product.php

открываем её и видим что запрос формируется таким образом:

public function getProducts($data = array()) {
...
$sql .= " AND p.model LIKE '" . $this->db->escape($data['filter_name'])."%'";
...
}

Как видим фильтр позволяет опускать только последнюю часть. Сделанно так неспроста и связанно это с тем как mysql обрабатывает запросы, а точнее с производительностью. Вдаваться в подробности не буду, пост не про это. Кому интересно читайте в справке mysql о том как работает LIKE оператор.

Что же делать, ведь всё же хочется иногда искать по выражению %Condition% - все просто, добавим условный оператор в запрос, при наличии которого будет искать по %Condition%, а во всех остальных случаях, по оригинальному Condition%. В качестве такого оператора, я выбрал знак восклицания. Меняем код так :

public function getProducts($data = array()) {
...
if (!empty($data['filter_name'])) {
    $searchCondition = $this->db->escape($data['filter_name']).'%';

    if (!empty($data['filter_name'][0]) && $data['filter_name'][0]=='!') {
        $data['filter_name'] = trim(substr($data['filter_name'],1));
        $searchCondition = '%'.$this->db->escape($data['filter_name']).'%';
    }
    $sql .= " AND pd.name LIKE '" .$searchCondition. "'";
...
}

Проверяем:

  1. Заходим в админку и создаем товар, с названием Test12345
  2. Пробуем вбить в фильтр в поле имени: Test - видим наш товар
  3. Пробуем вбить в фильтр в поле имени: t123 - не видим наш товар
  4. Пробуем вбить в фильтр в поле имени: ! t123 - видим наш товар

Аналогично меняем код для фильтра по модели товара:

if (!empty($data['filter_model'])) {
    $searchCondition = $this->db->escape($data['filter_model']).'%';
    if (!empty($data['filter_model'][0]) && $data['filter_model'][0]=='!') {
        $data['filter_model'] = trim(substr($data['filter_model'],1));
        $searchCondition = '%'.$this->db->escape($data['filter_model']).'%';
    }

    $sql .= " AND p.model LIKE '" . $searchCondition . "'";
}

Таким образом мы убили двух зайцев: сохранили старый функционал и добавили новый который не изменяет поведение старого.

Категории: Opencart