Интерфейс привязки фильтров к категориям

Всем привет!
За последний год уже дважды вставала задача сделать так, чтобы заказчик мог сам выбирать, какие фильтры выводить на странице категории. Объяснять ему, как задавать параметры в чанках, а тем более разрешать лезть в файлы (я всё делаю на Fenom и файлах) — ну совсем не торт. Поэтому я подумал о том, чтобы сделать визуальный редактор.
За час накидал, вдруг кому пригодится.

Нам потребуются:
  1. MIGX
  2. mFilter2
  3. 10 минут времени
Данное решение работает только с опциями. Я стараюсь использовать опции, а не поля ресурсов или товаров последнее время, т.к. заказчики обожают делать всё сами и им конечно же пофигу на производительность и прочее. У опций есть интерфейс и их легко встроить в любой товар.
Поэтому для начала нужно иметь список опций. Есть? Отлично, идём дальше.
Создаём новый элемент MIGX. В приложениях выбираем MIGX -> добавить элемент.
Называем его category_filters и сохраняем. Далее снова выбираем его правой кнопкой мыши и выбираем Импорт/Экспорт.
Вставляем следующий код:
{
  "formtabs":[
    {
      "MIGX_id":1,
      "caption":"\u0424\u0438\u043b\u044c\u0442\u0440\u044b",
      "print_before_tabs":"0",
      "fields":[
        {
          "MIGX_id":1,
          "field":"alias",
          "caption":"\u0418\u043c\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430",
          "description":"\u0412\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430",
          "description_is_code":"0",
          "inputTV":"",
          "inputTVtype":"",
          "validation":"",
          "configs":"",
          "restrictive_condition":"",
          "display":"",
          "sourceFrom":"config",
          "sources":"",
          "inputOptionValues":"",
          "default":"",
          "useDefaultIfEmpty":"0",
          "pos":1
        },
        {
          "MIGX_id":2,
          "field":"filter",
          "caption":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u0438\u043b\u044c\u0442\u0440",
          "description":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0437 \u0432\u044b\u043f\u0430\u0434\u0430\u044e\u0449\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u0430",
          "description_is_code":"0",
          "inputTV":"category_filter",
          "inputTVtype":"",
          "validation":"",
          "configs":"",
          "restrictive_condition":"",
          "display":"",
          "sourceFrom":"config",
          "sources":"",
          "inputOptionValues":"",
          "default":"",
          "useDefaultIfEmpty":"0",
          "pos":2
        },
        {
          "MIGX_id":3,
          "field":"type",
          "caption":"\u0422\u0438\u043f \u0444\u0438\u043b\u044c\u0442\u0440\u0430",
          "description":"\u0427\u0438\u0441\u043b\u043e\u0432\u043e\u0439 (\u043f\u043e\u043b\u0437\u0443\u043d\u043e\u043a) \u0438\u043b\u0438 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 (\u0433\u0430\u043b\u043e\u0447\u043a\u0438)",
          "description_is_code":"0",
          "inputTV":"",
          "inputTVtype":"listbox",
          "validation":"",
          "configs":"",
          "restrictive_condition":"",
          "display":"",
          "sourceFrom":"config",
          "sources":"",
          "inputOptionValues":"\u0427\u0438\u0441\u043b\u043e\u0432\u043e\u0439==number||\u041e\u0431\u044b\u0447\u043d\u044b\u0439==checkbox",
          "default":"",
          "useDefaultIfEmpty":"0",
          "pos":3
        }
      ],
      "pos":1
    }
  ],
  "contextmenus":"",
  "actionbuttons":"",
  "columnbuttons":"",
  "filters":"",
  "extended":{
    "migx_add":"",
    "disable_add_item":"",
    "add_items_directly":"",
    "formcaption":"",
    "update_win_title":"",
    "win_id":"",
    "maxRecords":"",
    "addNewItemAt":"bottom",
    "media_source_id":"",
    "multiple_formtabs":"",
    "multiple_formtabs_label":"",
    "multiple_formtabs_field":"",
    "multiple_formtabs_optionstext":"",
    "multiple_formtabs_optionsvalue":"",
    "actionbuttonsperrow":4,
    "winbuttonslist":"",
    "extrahandlers":"",
    "filtersperrow":4,
    "packageName":"",
    "classname":"",
    "task":"",
    "getlistsort":"",
    "getlistsortdir":"",
    "sortconfig":"",
    "gridpagesize":"",
    "use_custom_prefix":"0",
    "prefix":"",
    "grid":"",
    "gridload_mode":1,
    "check_resid":1,
    "check_resid_TV":"",
    "join_alias":"",
    "has_jointable":"yes",
    "getlistwhere":"",
    "joins":"",
    "hooksnippets":"",
    "cmpmaincaption":"",
    "cmptabcaption":"",
    "cmptabdescription":"",
    "cmptabcontroller":"",
    "winbuttons":"",
    "onsubmitsuccess":"",
    "submitparams":""
  },
  "columns":[
    {
      "MIGX_id":1,
      "header":"\u0424\u0438\u043b\u044c\u0442\u0440",
      "dataIndex":"alias",
      "width":"",
      "sortable":"false",
      "show_in_grid":1,
      "customrenderer":"",
      "renderer":"",
      "clickaction":"",
      "selectorconfig":"",
      "renderchunktpl":"",
      "renderoptions":"",
      "editor":""
    }
  ],
  "category":""
}


Далее создаём сниппет, назовём его getCatOptions.

Его код:

<?php
$options = $modx->getIterator('msOption');
$output = '';
foreach ($options as $opt){
    $values[] = $opt->get('caption').'=='.$opt->get('key');
}
$output = implode('||',$values);

return $output;
Далее создаём два ТВ — category_filter и category_filters.
Параметры первого:
Тип: Список (одиночный выбор)
Возможные значения:
@EVAL
$output = $modx->runSnippet('getCatOptions');
return $output;
Параметры второго:
Тип ввода: migx
Конфигурация: category_filters

Всё, на стороне админки закончили. Назначаем TV category_filters ко всем шаблонам, которые выполняют роль категорий товаров с фильтрами и пробуем что-то назначить.
Должно получится. Если где-то что-то не работает, пишите в комментарии, разберёмся :)
Поддерживается всего два типа ввода, числовые опции и чекбоксы. Можно сделать и больше, но тут уже на ваши плечи сия задача.
Теперь два последних приготовления.
Создаём сниппет getFilters
И его код:
<?php
//ваши стандартные для всех категорий параметры.
//обратите внимания на чанки (параметры tpl). Их тоже можно вынести в параметры, но мне как-то лень сейчас.
$default = [
    'element' => 'msProducts',
    'limit' => 9,
    'parents' => $page_id,
    'tplFilter.outer.price' => '@FILE:chunks/shop/category/filter/filter.number.outer.tpl',
    'tplFilter.row.price' => '@FILE:chunks/shop/category/filter/filter.number.row.tpl',
    'tplFilter.row.default' => '@FILE:chunks/shop/category/filter/filter.checkbox.tpl',
    'tplFilter.outer.default' => '@FILE:chunks/shop/category/filter/filter.checkbox.outer.tpl',
    'tpl' => '@FILE:chunks/shop/product/product.row.tpl',
    'tplOuter' => '@FILE:chunks/shop/category/filter/filter.outer.tpl',
    'ajaxMode' => 'scroll'
];

$aliases = 'ms|price==price';
$filters = 'ms|price:number';

if ($data == ''){
    $default['aliases'] = $aliases;
    $default['filters'] = $filters;
    $output = $default;
    return $output;
}

$tpls = [];
$fltrs = $modx->fromJSON($data);

foreach($fltrs as $filter){
    if ($filter['type'] == 'number'){
        $tpls['tplFilter.outer.'.$filter['filter']] = '@FILE:chunks/shop/category/filter/filter.number.outer.tpl';
        $tpls['tplFilter.row.'.$filter['filter']] = '@FILE:chunks/shop/category/filter/filter.number.row.tpl';
        $f = ',msoption|'.$filter['filter'].':number';
    } else {
        $f = ',msoption|'.$filter['filter'];
    }
    $a = ',msoption|'.$filter['filter'].'=='.$filter['filter'];
    $aliases .= $a;
    $filters .= $f;
}

$output = array_merge($default,$tpls);
$output['aliases'] = $aliases;
$output['filters'] = $filters;

return $output;

и теперь в чанке или шаблоне, который запускает наш mFilter2, пишем следующее:

{var $filters = $_modx->runSnippet('getFilters',[
     'page_id' => $_modx->resource.id,
     'data' => $_modx->resource.category_filters,
])}
{$_modx->runSnippet('!mFilter2',$filters)}
Дмитрий
20 апреля 2020, 19:10
modx.pro
2
1 240
+7
Поблагодарить автора Отправить деньги

Комментарии: 5

Павел Бигель
20 апреля 2020, 20:03
+3
Кстати да, хотелось бы такое решение из коробки замутить.
Но, я немного покритикую это тред:
@EVAL
$output = $modx->runSnippet('getCatOptions');
return $output;
EVAL — плохо. Даже разработчики MODX поняли что плохо и выпилили сие чудо в 3.0
Из этого следует что сниппет getCatOptions — в целом лишний, его можно заменить SQL запросом и вместо EVAL использовать SELECT.
Если же рассматривать сам сниппет, то
$options = $modx->getIterator('msOption');
я бы заменил на
$options = $modx->getIterator(msOption::class);
Опять же только с точки зрения того, чтобы не тратить время на это в момент выхода тройки.

Но в целом — спасибо за решение, кому-то однозначно пригодится.
    Дмитрий
    20 апреля 2020, 20:29
    0
    Выход тройки не за горами конечно, да.

    Насчёт Eval не знал, сделал по фасту как умел. Спасибо за подсказку :)
    Sergey (Sentinel)
    21 апреля 2020, 13:09
    0
    Зачем эти мороки, есть компонент modstore.pro/packages/ecommerce/msproductscomposerselection
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    5