Better Exposed Filters – изменяем диапазон значений ползунка Jquery UI Slider

21 мая 2014 - 17:32

Постановка задачи

Модуль Better Exposed Filters улучшает функциональность раскрытых фильтров Views в Drupal, предоставляя в частности, виджет Jquery UI Slider (ползунок) для раскрытого фильтра с диапазоном значений. Однако при этом имеется возможность указать только фиксированные значения «от» и «до» для фильтра, что в ряде случаев может нам не подойти. Например, если у нас сайт-каталог или интернет-магазин товаров, то логично для каждой категории товаров задавать минимальные и максимальные цены на товары из данной категории, а не всего каталога.

О том, как я решил подобную задачу на одном из проектов и пойдет речь в данной статье.

Настройка Better Exposed Filters

Рассмотрим настройку раскрытого фильтра по цене на примере магазина на Drupal Commerce. Устанавливаем модуль Better Exposed Filters, во вьюшке для товаров (ее создание и настройка не входят в рамки данной статьи) добавляем раскрытый фильтр по цене товара, как это показано на рисунке:

Настройка раскрытого фильтра по цене в Drupal Commerce

Далее в настройках раскрытых фильтров вьюшки выбираем Better Exposed Filters, в дополнительных настройках для фильтра по цене выбираем «Jquery UI Slider»:

Настройки Jquery UI Slider для Views Better Exposed Filters

Настройки можно оставить по умолчанию, так как диапазон значений мы будем задавать в коде. Можно указать размер шага Step ползунка.

Программное изменение минимального и максимального значений фильтра

Как оказалось, модуль Better Exposed Filters не предоставляет хука для изменения настроек плагина Jquery UI Slider, поэтому пришлось немного поломать голову, каким же образом задать нужные значения. В конце концов, остановился на варианте с хуком hook_js_alter():

function MYMODULE_js_alter(&$javascript) {
  $query = db_select('field_data_commerce_price');
  $query->addExpression('MIN(commerce_price_amount)');
  $min = $query->execute()->fetchField();
  $min_price = commerce_currency_amount_to_decimal($min, 'RUB');
  $query = db_select('field_data_commerce_price');
  $query->addExpression('MAX(commerce_price_amount)');
  $max = $query->execute()->fetchField();
  $max_price = commerce_currency_amount_to_decimal($max, 'RUB');
  foreach ($javascript['settings']['data'] as $key => $value) {
    if (array_key_exists('better_exposed_filters', $value)) {
      $javascript['settings']['data'][$key]['better_exposed_filters']['slider_options']['commerce_price_amount']['min'] = floor($min_price);
      $javascript['settings']['data'][$key]['better_exposed_filters']['slider_options']['commerce_price_amount']['max'] = ceil($max_price);
    }
  }
}

Приведенный выше код находит в базе данных сайта минимальную и максимальную цены товаров интернет-магазина на Drupal Commerce, округляет их и устанавливает в качестве интервальных значений фильтра. На использование именно хука hook_js_alter() меня натолкнуло то обстоятельство, что значения фильтра попадают в свойства settings js-объекта Drupal. Если у вас есть другие мысли по решению данной задачи, поделитесь ими, пожалуйста, в комментариях.

Комментарии

Подскажите, если сайт не на comerce и не нужно ничего округлять, то $min_price = commerce_currency_amount_to_decimal($min, 'RUB'); не нужно? Ну и макс,соответственно тоже...

Уже сам разобрался))) все заработало! Спасибо огромное!!! Только почему-то если обновить цену в ноде, то после сохранения значения становятся по дефолту... и только после чистки кеша появляются наши. Как поправить?

Сейчас все выводится, только если отредактировать ноду, то в блоке фильтров максимальное и минимальное становится опять по дефолту 0 и 100000... и только после чистки кеша появляются реальные максимальные и минимальные.

Я проверю и дополню статью, если обнаружу источник вашей проблемы. У вас блок с фильтрами есть на той же странице, что и сама нода? И приведите фрагмент вашего кода на всякий случай.

Блок есть.

function better_exposed_filters_js_alter(&$javascript) {
$query = db_select('field_data_field_price');
$query->addExpression('MIN(field_price_value)');
$min_price = $query->execute()->fetchField();
$query = db_select('field_data_field_price');
$query->addExpression('MAX(field_price_value)');
$max_price = $query->execute()->fetchField();
foreach ($javascript['settings']['data'] as $key => $value) {
if (array_key_exists('better_exposed_filters', $value)) {
$javascript['settings']['data'][$key]['better_exposed_filters']['slider_options']['field_price_value']['min'] = floor($min_price);
$javascript['settings']['data'][$key]['better_exposed_filters']['slider_options']['field_price_value']['max'] = ceil($max_price);
}
}
}

А вы что этот код поместили прямо в файл модуля Better Exposed Filters? Для этих целей нужно создавать свой кастомный модуль, ведь при обновлении все ваши изменения затрутся.

Подскажите как будет выглядеть код если необходимо сделать десятичный разделитель в минимальном и максимальном значении.
Пример
от 1 000 до 10 000

Решал такую задачу созданием специальных div-контейнеров, куда записывал начальные значения, отформатированные с помощью number_format(), затем в js-коде перехватывал события изменения положений ползунков (см. http://api.jqueryui.com/slider/), форматировал значения min и max уже в js и изменял соответственно значения в этих div, а поля ввода самого ползунка скрывал через CSS.